#author("2025-11-11T10:54:58+09:00","default:kouzouken","kouzouken") #author("2025-11-11T10:56:12+09:00","default:kouzouken","kouzouken") #contents **7/2 [#j417c702] 様々なCNNモデル(の組み合わせ4パターンは以下に記載)を試したが学習時間・精度ともに元々学習に使っていた"EfficientNetV2S", "DenseNet121", "MobileNetV3Large" の組み合わせが1番であったためこれから当面の間はMMLI.py と CNN+binning3.pyで錆画像の判別を行っていくこととする。 -グループA:"EfficientNetV2M", "InceptionResNetV2", "DenseNet121", "MobileNetV3Large" 高精度&バランス型 -グループB:"EfficientNetB3", "ResNet50", "MobileNetV3Small", "NASNetMobile" 軽量&高速重視型 -グループC:"InceptionResNetV2", "ResNet101", "EfficientNetV2S", "NASNetLarge" 深層学習&多スケール戦略型 -グループD:"EfficientNetV2S", "EfficientNetV2M", "EfficientNetB7", "InceptionResNetV2" 比較的新しい型のCNNモデル -CNN+binning3.pyについて(抜粋) --1, 学習画像と連続スコアの読み込み(回帰スコアが1.00点未満または5.01点以上の場合は学習に再利用・結果に保存しない) def load_images_and_scores(folder): #指定フォルダから画像とスコアを読み込む関数を定義 images, scores = [], [] #画像データとスコアを格納するリストを初期化 for fname in sorted(os.listdir(folder)): #フォルダ内のファイル名を並び替えて1つずつ処理 if fname.endswith(".png") or fname.endswith(".jpg"): #画像ファイル(png/jpg)のみ処理対象 try: #エラー処理のためのtryブロック score = float(fname[0]) #ファイル名の先頭文字からスコア(ラベル)を取得(例:「3_xxx.png」→3.0) score += np.random.uniform(-0.2, 0.2) #スコアにランダムなノイズを任意で設定しラベルをソフト化 if score < 1.0 or score > 5.0: #1.00点未満または5.01点以上の場合は学習に再利用・結果に保存しない print(f"⚠️ スコア範囲外: {fname} → {score:.2f} → スキップ") continue path = os.path.join(folder, fname) #画像ファイルのフルパスを作成 img = Image.open(path).convert("RGB").resize(IMAGE_SIZE) #画像を開いてRGBに変換し、指定サイズにリサイズ images.append(np.array(img) / 255.0) #画像をNumPy配列に変換し、0〜1に正規化してリストに追加 scores.append(score) #スコアをリストに追加 except: #例外が発生した場合の処理 print(f"スキップ: {fname}") return np.array(images), np.array(scores) #画像とスコアのリストをNumPy配列に変換して返す --2, CNN構築 def create_model(version): #指定したバージョン番号(インデックス)に対応するモデルを構築する関数の定義 input_img = Input(shape=(*IMAGE_SIZE, 3)) #入力画像の形状(IMAGE_SIZE, 3チャンネル)でKerasのInput層を作成 if version == 0: base = EfficientNetV2S(include_top=False, weights="imagenet", input_tensor=input_img) #モデル名がEfficientNetV2Sの場合の分岐 elif version == 1: base = DenseNet121(include_top=False, weights="imagenet", input_tensor=input_img) elif version == 2: base = MobileNetV3Large(include_top=False, weights="imagenet", input_tensor=input_img) base.trainable = False #ベースモデルの重みを凍結し転移学習で特徴抽出器として使用 x = base.output #ベースモデルの出力をxに代入 x = layers.GlobalAveragePooling2D()(x) #グローバル平均プーリング層で特徴マップを1次元ベクトルに変換 x = layers.Dense(2048, activation='relu')(x) #全結合層(2048ユニット、ReLU活性化)を追加 x = layers.BatchNormalization()(x) #バッチ正規化層を追加し学習を安定化 x = layers.Dropout(0.5)(x) #ドロップアウト(50%)で過学習を防止 . . . output = layers.Dense(1)(x) #出力層(1ユニット、活性化なし=線形)を追加 output = ClipLayer()(output) #出力値を1.0〜5.0にクリップするカスタムレイヤー(ClipLayer)を適用 model = Model(inputs=input_img, outputs=output) #入力と出力を指定してKerasのModelを構築 model.compile(optimizer='adam', loss=custom_penalizing_loss, metrics=['mae']) #モデルをAdam最適化, カスタム損失関数, MAE評価指標でコンパイル return model #構築したモデルを返す --3, 回帰スコアとモデル学習の設定 # === binning: 回帰スコア → 評点1〜5 === →回帰モデルの連続値出力を1〜5の整数評価に変換する関数を定義 def binning(score): #binning関数の定義 if score < 2.3: return 1 elif score < 3.1: return 2 elif score < 3.4: return 3 elif score < 3.7: return 4 else: return 5 # === モデル学習 === →データを読み込み、訓練・検証セットに分割し、全ての選択モデルについて構築・学習・保存を行う x, y = load_images_and_scores(TRAIN_FOLDER) #学習用画像とスコアを読み込み x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.2, random_state=42) #データセットを訓練用と検証用(test_size)に8:2で分割 model = create_model(i) #モデルを構築 . . . model.save(MODEL_PATHS[i]) #学習済みモデルを保存 print(f"✅ モデル{i+1}保存完了: {MODEL_PATHS[i]}" --4, 回帰スコアから評点の予測 # === 評点予測・保存 === →アンサンブル学習済みモデルでテスト画像を予測し、平均スコアを整数評点に変換、評点ごとに画像を保存。予測過程を出力 models_ensemble = [ #アンサンブル用に複数の学習済みモデルをリストとして読み込み tf.keras.models.load_model(p, compile=False) #各モデルファイル(パスp)を、カスタムレイヤーClipLayerを認識できるようにして読み込み for p in MODEL_PATHS #上記の読み込みを、全てのモデルファイルに対して実行 ] test_fnames = sorted([f for f in os.listdir(TEST_FOLDER) if f.endswith('.png') or f.endswith('.jpg')]) #テスト用フォルダ内の画像ファイル名(png/jpg)を取得して並び替え os.makedirs(OUTPUT_BASE, exist_ok=True) #出力用のベースフォルダを作成(既に存在してもエラーにならない) for i in range(1, 6): #評点1〜5の各スコア用サブフォルダを作成するためのループ os.makedirs(os.path.join(OUTPUT_BASE, f"score{i}"), exist_ok=True) #各スコアごとの出力サブフォルダを作成 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- for fname in test_fnames: #テスト画像ごとにループ path = os.path.join(TEST_FOLDER, fname) #画像ファイルのフルパスを作成 img = Image.open(path).convert("RGB").resize(IMAGE_SIZE) #画像を開き、RGB化・リサイズ x_input = np.array(img) / 255.0 #画像をNumPy配列に変換し正規化 x_input = x_input.reshape(1, *IMAGE_SIZE, 3) #モデル入力用に配列の形状を調整(バッチ次元追加) preds = [model.predict(x_input, verbose=0)[0][0] for model in models_ensemble] #各モデルで画像を予測しスコアをリスト化 avg_score = np.mean(preds) #各モデルの予測スコアの平均値を計算します(アンサンブル平均) final_score = binning(avg_score) #平均スコアを1〜5の整数評点に変換 save_path = os.path.join(OUTPUT_BASE, f"score{final_score}", fname) #評点ごとのサブフォルダに保存するためのパスを作成 img.save(save_path) #画像を該当サブフォルダに保存 print(f"📂 {fname} → 回帰スコア: {avg_score:.2f} → 評点: {final_score}") #画像名・回帰スコア・最終評点を表示 # === 検証評価 === →検証データでアンサンブル予測を行い、平均スコアを整数評点に変換して正解と比較できる形に整形する val_preds = [model.predict(x_val, verbose=0).flatten() for model in models_ensemble] #検証データに対して各モデルで予測し、スコア配列をリスト化 y_val_pred = np.mean(val_preds, axis=0) #各サンプルについて全モデルのスコア平均を計算 y_val_bin = np.array([binning(s) for s in y_val_pred]) #平均スコアを1〜5の整数評点に変換 y_true_bin = np.array([binning(s) for s in y_val]) #正解スコアも同様に整数評点に変換 **6/23 [#j417c623] -錆の画像から評点を判断するのに用いるスクリプト --MMLI (=More Machine Learning Images).py … 既存の画像を反転・回転等で学習させる画像枚数を増やすスクリプトのこと。 --CNN+binning , 特徴 / スクリプト名 , CNN+binning.py , CNN+binning2.py , CNN+binning3.py , CNN+binning4.py , モデルの種類 , オリジナルCNN3種 , 強化CNN(層数増加) , EfficientNetV2S / DenseNet121 / MobileNetV3 , 同左 , アンサンブル学習 , ○ , ○ , ○ , ○ , 出力形式 , 回帰+binning , 回帰+binning , 回帰+binning , 回帰+Sigmoid正規化+binning , Soft Labeling(評点ぼかし) , ○ , ○ , ○ , ○ , 損失関数 , MSE+重みペナルティ , 強化ペナルティ損失関数 , 同左 , 同左 , 特徴量の利用 , × , × , × , ○(画像+数値特徴:面積、粒径、Sobel量) , 特徴量融合方法 , ー , ー , ー , CNN出力ベクトル+数値特徴 → Dense結合 , 出力スケーリング , × , × , × , ○(シグモイド関数+0〜5.0スケーリング) , カスタムbinning関数 , ○ , ○ , ○ , ○ , UMAP可視化 , ○ , ○ , ○ , ○ , 判別補正(3⇔4の補正など) , × , × , × , ○(特徴量条件付き補正が可能な構造) , 使用層の深さ・複雑さ , 普通 , やや深め , 深層+転移学習モデル , 同左 **手書き数字の画像をCNNで機械学習するのに必要な画像とPythonファイル [#bfidf3ca] -/home/kouzou/Morii25/hikitugi 内にある画像とPythonファイルについて ***5/21 [#j417c521] -- 1, Mnist-Hanbetsu-Full_auto4.py (Ver4.0) … Ver3.0ではモデルの学習回数を1回としていたが、より高い精度を出すために学習回数を3回としその中から最も信頼できるモデルを画像ごとに取り出して機械が自動的に判別できるようにした。 -- 2, Mnist-Hanbetsu-Full_auto5.py (Ver5.0) … Ver4.0からCNNの学習方法を毎回変えるようにした(アンサンブル学習)。アンサンブル学習の導入により構造の異なる学習モデルを使うことで特徴の学習傾向に多様性を持たせることができ、全体の分類精度を高めやすくなる。今までは畳込みとプーリングの層を3層にして計3回学習させる方法を取っていたが、今回から3回とも畳込みとプーリング層の変化や関数の有無を交えながら画像の判別をさせるようにした。 --1回目の学習は畳込みとプーリング層が1層の最もシンプルな基本形とし、パラメータも少なめで学習が早いというメリットがある一方で過学習しやすいという側面がある。 --2回目は1回目よりも畳込み層を深くしたことに加えて正則化 (BatchNormalization) も組み込んだ。これにより複雑な特徴を抽出しやすく過学習を抑制できる。 --3回目はより学習させる範囲を大きくし正則化を強化させた学習モデルとした。→ 大きめのフィルターで広範囲の特徴を抽出。 -各モデルの構造比較(表) ,項目 ,モデル1 ,モデル2 ,モデル3 ,畳み込み層 ,1層 ,2層 ,2層 ,プーリング ,1回 ,1回 ,1回 ,正規化 ,× ,〇 ,〇 ,Dropout ,× ,〇(1回),〇(2回) ,Dense層,64ユニット ,128ユニット ,64ユニット ,出力層 ,softmax(10) ,softmax(10) ,softmax(10) ,学習の安定性 ,弱い(過学習しやすい) ,普通(汎化性能〇) ,強い(深く・安定) ただVer5.0は様々な学習要素を詰め込んでしまったのが原因なのか判別精度がかえって落ちてしまったため、今後手書き数字の画像判別を行なう際にはVer4.0のスクリプトで実行することを勧めておく。 ***5/14 [#j417c512] -新たに作成したPythonファイルについて説明していく --1, Gazou-Kakou.py … 判別させたい画像に点や直線, 円や多角形等の図形を加えて画像処理させるためのものであり、わざと数字を見せづらくすることで正確に読み取ることができるのか確かめるためのもの。点の数や多角形の頂点を増減したい場合は一括処理関数の "処理を適用:ランダムで描画" の部分を書き換える必要がある。ちなみにこのスクリプトで画像加工すると以下のようになる &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/0_0.png,150x); ー画像加工→ &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/modified_0_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/5_0.png,150x); ー画像加工→ &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/modified_5_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/7_2.png,150x); ー画像加工→ &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250508/modified_7_2.png,150x); ---1-1, Gazou-Kakou-Sen_only.py … 上記のGazou-Kakou.pyから "線だけ" を加えた画像処理を行うスクリプトのこと。 ---1-2, Gazou-Kakou-Ten_only.py … 上記のGazou-Kakou.pyから "点だけ" を加えた画像処理を行うスクリプトのこと。 ---1-3, Gazou-Kakou-Zukei_only.py … 上記のGazou-Kakou.pyから "図形だけ" を加えた画像処理を行うスクリプトのこと。 ---※1-1~3 はGazou-Kakou.py内において "処理を適用:ランダムで描画" の部分からでも "#"の有無で自身の好きなようにカスタムできる。 --2, Suuji-Gazou-Seisei.py … 0~9までの数字を自動で生成するスクリプト。一応手書き風文字フォント(KGPrimaryPenmanship.ttf)を使用しているが、似通った文字が複数枚生成されてしまうため使用はあまりおすすめしない。もし自身で画像をペイントツール等を使用して作成するのが面倒だと感じる場合は後述する "Mnist-tyuusyutsu.py" の使用を勧める。 --3, Mnist-tyuusyutsu.py … Mnist上にある画像データを抽出して保存するスクリプトで、保存する枚数は任意で決められる。("画像1クラスあたりの保存枚数"で変更可能) --4, Hanbetsu-Full_auto.py … 判別画像の処理からそれらの判別及び数字ごとの正答率をテキストファイルに保存するまでの流れを一括で行えるようにしたスクリプトで、Mnist.pyを実行した後にこれを使用することになる。判別画像の名前は ○_1.png または ○_1.jpg とつけておく必要がある。丸には画像の数字を入力する必要があり、後ろの数字には何枚目なのかを記すこと。例) 9_8.png … "9"と書かれた画像の"8"番目 --5, Mnist-Hanbetsu-Full_auto.py … 上記の Hanbetsu-Full_auto.py にMnistの機械学習を付け加えたスクリプトで基本的に指定されたフォルダ(デフォでは "digits_dataset" になっている)を作成しあとはこのスクリプトを実行するだけで正答率をテキストファイルに保存するまでを自動的に行ってくれる。またMnistを用いた機械学習を行うごとに結果がh5ファイルとして保存されると思うが、ファイルを消さずに上書きという形で学習させるとかえって正答率が下ってしまう過学習という状況が発生しかねないので常に新しいh5ファイルが作成されるようにした。(それまでのファイルは削除されてしまうので注意) --6, Mnist-Hanbetsu-Full_auto2.py … 5の Mnist-Hanbetsu-Full_auto.py ではデータ(Mnist)を画像処理することなく(下図左側3枚)機械学習させていたが、こちらではMnistのデータを下図右側3枚(イメージ 実際に学習する画像とは異なる)のように画像処理を行ってから機械学習させた。こうすることで識別率を上げることができる。 &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/1_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/4_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/9_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/noised_1_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/noised_4_0.png,150x); &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250513/noised_9_0.png,150x); ***5/7 [#j417c507] --1~9 のjpg画像 … CNNで手書き数字を機械学習させたものに数字を判別させるためのもの。ただし判別精度は良くない。 --mnist.py … mnistという6万枚の手書き数字データ(学習用が5万枚, 判別用が1万枚)を学習してそれがどのくらいの精度で正解できたかをMATE端末上で表示し、その結果を h5 ファイルとして保存するものである。 --mnist-hosei.py … mnist.pyとの違いは画像が傾いていたり文字が歪んでいたりしている状態で機械学習させる事だけで、その他は mnist.py と同じ。 --Hukusuu-hanbetsu.py … mnist.py または mnist-hosei.py を実行した後に用いるファイルで、複数枚の画像を一度に判別するプログラムコードが書かれている。あらかじめ保存しておいた h5 ファイルの名前や複数枚の画像を保存したファイル名が一致しているか確認しておく必要がある。 --Suuji-kakuritsu.py … mnist.py または mnist-hosei.py を実行した後に用いるファイルで、ある1枚に書かれた数字を機械が何と判別したか またその判別結果にはどのくらいの精度があるかをMATE端末上で表示されるものである。あらかじめ保存しておいた h5 ファイルの名前や判別したい画像のファイル名がプログラムコードと一致しているか確認しておく必要がある。 --kani.py … mnist.py または mnist-hosei.py を実行した後に用いるファイルで、判別した画像に何の数字(0 ~ 9)が描かれているかに加えてどのくらいの信頼度があるかをグラフ化した画像が表示されるものである。あらかじめ保存しておいた h5 ファイルの名前や判別したい画像のファイル名がプログラムコードと一致しているか確認しておく必要がある。 --suuji-hanbetsu.py … mnist.py または mnist-hosei.py を実行した後に用いるファイルで、ある1枚に書かれた数字を機械が何と判別したかを確認する。あらかじめ保存しておいた h5 ファイルの名前や判別したい画像のファイル名がプログラムコードと一致しているか確認しておく必要がある。 **錆の写真からSVMで機械学習するまでの流れ [#g1ddf3ca] この一連の流れは同じファイル内に保存するのが望ましい -(1) RGBrironti.py について (コマンド) --1, MATE端末を開き py ファイルのある hikitugi に移動する (cd hikitugi) --2, py ファイルを呼び出す (python RGByomitori.py) --3, フォルダーのパス入力が求められるので錆の写真があるフォルダーを入力する → Enterキー (一例: /home/kouzou/sato24/gr/data/1_ppm) --4, 保存するファイル名を入力する → Enterキー (一例: /home/kouzou/sato24/gr/data/1_ppm/RGBkekka.csv ) この場合錆の写真があるフォルダーに "クリックした座標" , "RGB値"の結果がcsv形式で保存される。 --5, その後ウィンドウのどこかしらに錆の画像が表示されるので、カラースケールの "シアン" , "イエロー" , "マゼンタ" の部分を順番にクリックする。そうするとMATE端末上に [ 画像: ファイル名(.ppm) | クリック位置: (x座標, y座標), RGB値: (R(0~255), G(0~255), B(0~255),) ] と表示される。 --6, ファイル内にある写真すべての "シアン" , "イエロー" , "マゼンタ" の部分を順番にクリックする。すべての写真で "シアン" , "イエロー" , "マゼンタ" の部分をクリックすると下のようになり、これと同じような感じでcsvファイルにも保存されている。 &ref(http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250428-1.png,600x); ---補足:保存するファイル名を以前作成したファイル名と同じにすると新たに上書きされるのではなく、既存のデータに加えられる形で保存される。 -(2) matrix.py について (コマンド) --1, MATE端末を開き py ファイルのある hikitugi に移動する (cd hikitugi) ←必要であれば --2, py ファイルを呼び出す (python matrix.py) → Enterキー --3, 結果を書き込むファイルを入力してください と表示されるので入力する。ここでは保存する場所に加えて結果ファイルの名前も”名前 + .txt ”と入力する必要がある。(例: kekka.txt) → Enterキー --4, シアン・イエロー・マゼンタの各RGB値を入力してください と表示されるので入力する。(0~255 の間) → 入力する値はどれか1枚の写真における"シアン" , "イエロー" , "マゼンタ" のRGB値 -(3) henkan.py について (コマンド) --1, MATE端末を開き py ファイルのある hikitugi に移動する (cd hikitugi) ←必要であれば --2, py ファイルを呼び出す (python henkan.py) --3, 変換行列ファイルのパスを入力してください と表示されるので(2)で作成したtxtファイルを入力する(一例: kekka.txt ) → Enterキー --4, 補正したいPPM画像のパスを入力してください と表示されるのでppm画像のパス入力する。(例: rating○_○.ppm) → Enterキー ---基本的に/home/kouzou/sato24/gr/data内にある ○_ppm や hg_○_ppm と書かれたファイルから選択するはず (例: /home/kouzou/sato24/gr/data/1_ppm/rating1_1.ppm) --5, 補正後の画像を保存するパスを入力してください と表示されるので変換後のppm画像をどのパスに保存したいか入力する (例: henkan-after.ppm と入力すると henkan-after というppmファイルが保存される) → Enterキー -(4) グレースケール化とedge.py について(コマンド) --1, $ convert rating○_○.ppm -colorspace Gray gray.pgm → gimp と打って Enterキー --2, py ファイルを呼び出す (python edge.py) --3, 入力ディレクトリパスの入力が求められる。なお edge.py はpgmファイルのみ対応しているのでpgmファイルで保存されていいる写真のあるフォルダー名を入力する → Enterキー (一例: edge) --4, 出力ディレクトリパスの入力が求められる。ここでは3で入力したファイルをエッジ処理したものをどこに保存するかを設定する。ここでは一例として(edge_150)と入力する。 --- 3のedgeはエッジ処理したものを入れておくファイル名であり、もしファイル名を入れずにEnterキーを押してしまうとこの場合hikitugiのフォルダー内に保存されてしまうので注意すること。 --5, エッジ処理の閾値1, 2を入力してくださいと表示される ---エッジ処理の閾値1は "Cannyエッジ検出の「低い方のしきい値」" のことである。画像の中で「ここはエッジ」と判断するための数値で、50〜300くらい(50刻み)に設定しておくとよい。 ---エッジ処理の閾値2は "Cannyエッジ検出の「高い方のしきい値」" であり、100〜400くらい に設定しておくとよい。 --6, 閾値1, 2 を入力して Enterキー を押してしばらくするとhikitugi内にedge_150というフォルダーが作成される。そのフォルダーを開いて錆の写真が左下 (edge) から右下 (edge_150) のような感じに変換されていれば成功。 http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250424-2.png → http://www.str.ce.akita-u.ac.jp/~gotouhan/j2024/morii/20250424-3.png -(5) svm.py について (コマンド) --1, py ファイルを呼び出す前に svm.py ファイル(編集画面)を開く --2, ファイルを開いたら13行目に書いてある data_dir = 〜 の部分を確認する。data_dir = ( "〜" ) の部分を " edge " に変更してclass_namesも定義する必要があり、そうしないとエラーがでる。今回はclass_namesを作成・定義してこなかったので佐藤さんが作成した 150_300_edge を機械学習= svm させる。 ---① 150_300_edgeファイルをhikitugiファイル内にコピーする。 ---② 編集画面の13行目: data_dir = "150_300_edge" と編集する。 ---③ 編集画面の15行目: class_names = ["mk1_pgm", "mk2_pgm", "mk3_pgm", "mk4_pgm"] になっているのを確認する。 --3, 編集が完了したら python svm.py と入力 → Enterキー **GIMPでの画像の切り取り方(200×200ピクセルの場合) [#xccc5194] --1, GIMPで画像を開き上のツールバーから 画像 → ガイド → 新規ガイド と進む --2, 新規ガイドを選択すると方向と位置を決めることができ、初めは方向を"水平"として位置を"200"に設定する。そうすると画像に青の破線が表示されたと思う。再びツールバーから 画像 → ガイド → 新規ガイド と進み、方向を"水平"位置を"400"に設定する。今度は前に引かれた青の破線よりも200ピクセル分右側に破線が引かれた。これを "600" , "800" , ...と繰り返していく --3, 水平方向に200ピクセル間隔で線を引けたら次に垂直方向に200ピクセル間隔で線を引いていく。ツールーバーから 画像 → ガイド → 新規ガイド と進み、方向を"垂直" 位置を"200"とする。水平の時と同様に位置を200ずつ変えながら線を引いていくと碁盤状に線が引けたのではないかと思う。 --4, そうしたらツールバーから 画像 → ガイドを使用して切り分け と選択すると200×200ピクセルで分割した画像が出来上がる。 *環境整備 [#k8c8c048] **Anaconda [#caa0c13b] インストールが終了すると Do you wish to update your shell profile to automatically initialize conda? This will activate conda on startup and change the command prompt when activated. If you'd prefer that conda's base environment not be activated on startup, run the following command when conda is activated: conda config --set auto_activate_base false You can undo this by running `conda init --reverse $SHELL`? [yes|no] みたいに出る。 「yes」を選ぶと、condaが自動で初期化され、端末を開いた時点でbase環境がアクティブになります。プロンプト表示 内容も変わります。 「no」を選ぶと、通常はcondaコマンドを手動で有効化(例:conda activate base)する必要があります。 ということなので、パソコン起動時から常駐されたくないから、noを選ぶ。 .bashrcを.bashrc.bakにバックアップを取ってから、末尾に export PATH=/home/kouzou/anaconda3/bin:$PATH みたいに書き込む(ユーザー名がkouzouの場合)。 で、bashrcを有効にするために、 $. .bashrc と打ち込んでから(あるいはターミナルを起動し直して)、condaと打ち込んで usage: conda [-h] [-v] [--no-plugins] [-V] COMMAND ... みたいに出ればよい。 **vscode [#x0577917] $ sudo snap install --classic code codeと打ち込んで起動できればよい。 view→Command Palette...検索欄>にdisplayと打ち込んで、Configure Display Languageを選択→日本語を選択 **tensorflow [#a485236d] $ pip install --upgrade pip $ pip install tensorflow pipはPython用パッケージマネージャー。 インストール終了したら、pythonと打ち込んで、Python対話モードを起動する。 プロンプトが>>>になったら、以下のように入力してみる。 エラーが出ずにバージョンが表示されればよい。 >>> import tensorflow as tf >>> print(tf.__version__) *ランダム性 [#l36e93b0] 以下のプログラムで、まずset1でEfficientNetV2S、DenseNet121、MobileNetV3Largeのモデルをそれぞれ計算し、その後にアンサンブル学習を行っています。次に、set2でInceptionResNet、XceptionV2、DenseNet121のモデルをそれぞれ計算し、その後にアンサンブル学習を行っています。このとき、set1でまず計算したDenseNet121の結果(mae_scoreやacc_score)が、set2で計算したDenseNet121の結果(mae_scoreやacc_score)と数パーセントずれるのはなぜですか。 非常に良い質問です。 DenseNet121 を両方のセット(set1・set2)で使っているのに、 MAE や Accuracy が数%ずれるのは ―― スクリプト上で理論的には「同じ条件」に見えても、 実際には完全な再現性が保証されていないためです。 以下に、なぜ差が生じるのかを技術的に解説します。 🧩 差が出る主な原因 ① モデル初期化のランダム性 base = DenseNet121(include_top=False, weights="imagenet", input_tensor=input_img) weights="imagenet" で初期化する場合、重みは固定されていますが、 上に積んでいる全結合層 (Dense, Dropout, BatchNormalization) は毎回ランダム初期化されます。 ➡️ set1 と set2 で 別インスタンスの DenseNet121 を作成しているため、 上位層の重み初期値が異なり、学習曲線がわずかに変化します。 ② Data Augmentation と Soft Labeling の乱数 コード内で次の処理があります👇 score += np.random.uniform(-0.2, 0.2) # Soft Labeling また、Data Augmentation(ランダム回転・明度変換など)を適用している場合: tf.image.random_brightness, random_contrast, random_hue ... ➡️ これらのランダム性は、Python・NumPy の seed 固定だけでは完全に抑止できません。 TensorFlow 内部のデータパイプラインが独自に乱数を生成するためです。 ③ 学習過程における GPU / 並列演算の非決定性 TensorFlow + cuDNN(GPU)の場合、 特定のレイヤー(特に BatchNormalization, Dropout)で 計算順序や浮動小数誤差の違いにより、結果が微妙に変わります。 例:同じ初期値でも、計算を別のGPUスレッドで行うと小数第6位以降が変化 → それが累積して MAE/ACC が ±1〜3% 変化することも。 ④ 2回目の DenseNet121 の学習が完全に独立 set1 と set2 は以下のように完全に独立して実行されています: result1 = run_experiment(..., model_set="set1") result2 = run_experiment(..., model_set="set2") そのため、set2 の DenseNet121 は set1 の学習結果を引き継いでいません。 たとえ構造が同じでも、別々に初期化・最適化・EarlyStopping されているため、 誤差1〜3%のズレはごく自然な範囲です。 🧠 つまり — DenseNet121 の差は「同じモデル構造でも別の学習経路を通った」ため set1 と set2 の DenseNet121 は パラメータ初期値が異なる別個体。 Soft Labeling と Data Augmentation のランダム性も加わり、 → MAE, Accuracy, F1 が数%ずれる。 ✅ 差を最小化したい場合の対策 対策 内容 ① 乱数の完全固定 random.seed, np.random.seed, tf.random.set_seed に加え、 os.environ["TF_DETERMINISTIC_OPS"]="1" を設定(あなたのスクリプトはOK) ② Data Augmentation を停止 ランダム操作をすべて無効化して「同一データ」で学習 ③ Soft Labeling を固定化 np.random.uniform(-0.2, 0.2) をやめるか、固定値に置き換える ④ GPUを無効化 CPU演算は遅いが、決定論的に動作するため再現性が高い ⑤ 同一モデルを再利用 set2でDenseNet121を再インスタンス化せず、set1の学習済みモデルを再評価する 📘 まとめ(Wiki掲載用に適した説明) DenseNet121 の結果が set1 と set2 で数%異なる理由 同一モデル構造を使用していても、学習の初期化やデータのランダム性により結果が完全には一致しません。 具体的には、①重みの初期化、②Soft LabelingやData Augmentationの乱数、③GPUの並列演算における非決定性などが影響します。 そのため、全く同じ条件で再現性を保証するには、乱数固定に加え、データ拡張を停止し、GPUではなくCPUモードで決定論的演算を行う必要があります。 もし希望があれば、 「このスクリプトで完全に再現性を確保する設定(CPU強制・Augmentation無効・ラベル固定)」 を実装した改良版も書けます。 作成しましょうか? あなた: アンサンブル学習ではなく、DenseNet121のモデルを単独で計算させた場合でも、計算するたびに、こうしたランダム性による影響で毎回、結果が数パーセント変わりますか。あるいは、アンサンブル学習に特有の現象ですか ChatGPT: 非常に鋭い質問です。 結論から言うと―― はい、DenseNet121を単独で計算した場合でも、毎回結果は数%程度変動します。 これはアンサンブル特有の現象ではなく、ニューラルネットワークの学習全般に内在する「非決定性(nondeterminism)」 に起因するものです。 🧩 単独モデル(例:DenseNet121単体)でも結果が変わる主な理由 ① 重み初期値のランダム性 Keras/TensorFlow は各層(Dense, BatchNormなど)の初期重みをランダムで設定します。 x = layers.Dense(512, activation="relu")(x) たとえ同じデータ・同じエポック数でも、 初期重みがわずかに違えば、最終的に収束する重みが異なるため、 MAEやAccuracyに1〜3%(場合によっては5%)ほど差が出ます。 ② Data Augmentationのランダム性 tf.image.random_brightness, random_flip, random_hue などを使うと、 各エポックで入力データがランダムに変化します。 このため: 同じ画像が「異なる明るさ・彩度」で学習される 結果として学習経路が毎回少しずつ異なる ➡️ 同じDenseNet121でも、毎回微妙に異なる学習パターンになります。 ③ GPU演算の非決定性 TensorFlow + cuDNN (GPU) では以下のような「非決定的な演算」が含まれます。 並列演算の順序が変わる(浮動小数誤差が累積) BatchNormalization や Dropout の内部処理にランダム性がある これにより、全く同じコードを2回実行しても結果が異なることがあります。 特に DenseNet は BatchNorm を大量に含むため、影響が顕著です。 ④ EarlyStopping や Validation Split の影響 EarlyStopping もまたランダム分割 (train_test_split) に依存しています。 例えば random_state=42 を固定しても、Augmentation や Dropout のタイミングで validation loss の最小点がずれることがあります。 🧠 対して「アンサンブル学習」ではどうなるか アンサンブル(複数モデルの平均)はむしろ個々のランダム性を平均化して安定化します。 したがって: 単独モデルよりも結果は安定しやすい しかし、「同じアンサンブルを2回作る」ときには、やはり個々のモデルの初期値が異なるため、再現性は完全ではない ✅ まとめ(Wiki掲載用にも適した説明) DenseNet121 単独でも再現性は完全ではない ニューラルネットワークの学習には、初期重み、Data Augmentation、GPU演算、Dropoutなど多くのランダム要素が含まれます。そのため、同一条件でDenseNet121を単独で学習させても、結果(MAEやAccuracyなど)が数パーセント変動することがあります。 一方、アンサンブル学習はこれらのランダム性を平均化して安定させる効果がありますが、アンサンブル全体を再構築する場合も完全な再現性は保証されません。 もし「DenseNet121の結果を完全に再現」したい場合は、 次のような設定を行うとよいです👇 import os, random, numpy as np, tensorflow as tf os.environ["PYTHONHASHSEED"] = "42" os.environ["TF_DETERMINISTIC_OPS"] = "1" os.environ["TF_CUDNN_DETERMINISTIC"] = "1" random.seed(42) np.random.seed(42) tf.random.set_seed(42) # GPUではなくCPUで実行 tf.config.set_visible_devices([], 'GPU') これでランダム性を最大限抑えられます。 (ただしCPUのみ実行になるため学習時間は大幅に増えます。) ご希望があれば、「DenseNet121を完全再現性モードで1回ずつ学習・評価する最小スクリプト」を示しますが、作成してみましょうか?