導入こんにちは!数時間前にディープラーニング プロジェクトを終えたので、その成果を共有したいと思います。このチャレンジの目的は、人が肺炎にかかっているかどうかを判断することです。もしそうなら、それが細菌によるものかウイルスによるものかを判断します。そうですね、このプロジェクトは検出ではなく分類と呼ぶべきだと思います。 つまり、このタスクは、ラベル名が「正常」、「ウイルス」、「細菌」であるマルチ分類問題になります。この問題を解決するために、優れた画像分類機能を備えた CNN (畳み込みニューラル ネットワーク) を使用します。それだけでなく、ここではモデルのパフォーマンスを向上させるために画像拡張技術も実装しました。ちなみに、テストデータでは 80% の精度が得られ、非常に印象的でした。 データセット全体のサイズは約 1 GB なので、ダウンロードには時間がかかる場合があります。あるいは、Kaggle Notebook を直接作成し、そこにプロジェクト全体をコーディングすることもできるので、何もダウンロードする必要もありません。次に、データセット フォルダーを参照すると、train、test、val という 3 つのサブフォルダーがあることがわかります。 まあ、フォルダ名は一目瞭然だと思います。さらに、train フォルダー内のデータには、それぞれ通常、ウイルス、細菌のカテゴリのサンプルが 1341、1345、2530 個含まれています。私が言いたいことはこれですべてだと思います。それではコードを見ていきましょう。 注: このプロジェクトで使用したコード全体をこの記事の最後に掲載しました。 モジュールとトレーニング画像の読み込みコンピューター ビジョン プロジェクトで作業する場合、最初に行うことは、必要なすべてのモジュールと画像データ自体をロードすることです。進捗バーを表示するために tqdm モジュールを使用しますが、これがなぜ便利なのかは後で説明します。 最後にインポートしたのは、Keras モジュールからの ImageDataGenerator です。このモジュールは、トレーニング プロセス中に画像拡張技術を実装するのに役立ちます。 - インポートOS
- cv2import pickleimport numpyをnpとしてインポートします
- matplotlib.pyplot をpltとしてインポートします。
- Seaborn をSNSとしてインポートする
- tqdmからtqdm をインポート
- sklearn.preprocessingからOneHotEncoder をインポートします
- sklearn.metricsからconfusing_matrix をインポートします
- keras.modelsからモデルをインポートし、load_model を実行します。
- keras.layersからDense、Input、Conv2D、MaxPool2D、Flatten をインポートします。
- keras.preprocessing.imageからImageDataGeneratornp.random.seed(22) をインポートします。
次に、各フォルダーから画像データを読み込むための 2 つの関数を定義します。一見すると、以下の 2 つの関数はまったく同じに見えますが、実際には太字で強調表示されている行にいくつかの違いがあります。これは、NORMAL フォルダーと PNEUMONIA フォルダー内のファイル名の構造が若干異なるために行われます。違いはあるものの、両方の機能によって実行されるその他のプロセスは本質的に同じです。 まず、すべての画像のサイズを 200 x 200 ピクセルに変更します。 すべてのフォルダー内の画像の次元は異なり、ニューラル ネットワークは固定の配列サイズのデータしか受け入れることができないため、これは重要です。 次に、基本的にすべての画像は 3 つのカラー チャネルで保存されますが、これは X 線画像の場合は冗長です。したがって、私のアイデアは、これらのカラー画像を両方ともグレースケールに変換することです。 - #最後のスラッシュを忘れずに
- def load_normal(norm_path): norm_files = np.array(os.listdir(norm_path)) norm_labels = np.array([ 'normal' ]*len(norm_files))
- norm_images = [] tqdm(norm_files)内の画像の場合:
- 画像 = cv2.imread(norm_path + 画像) 画像 = cv2.resize(画像, dsize=(200,200))
- 画像 = cv2.cvtColor(画像, cv2.COLOR_BGR2GRAY) norm_images.append(画像)
- norm_images = np.array(norm_images) norm_images、norm_labelsを返す
- def load_pneumonia(pneu_path): pneu_files = np.array(os.listdir(pneu_path)) pneu_labels = np.array([pneu_file.split( '_' )[1] for pneu_files in pneu_files])
- pneu_images = [] tqdm(pneu_files)内の画像の場合:
- 画像 = cv2.imread(pneu_path + 画像) 画像 = cv2.resize(画像、dsize=(200,200))
- 画像 = cv2.cvtColor(画像、cv2.COLOR_BGR2GRAY) pneu_images.append(画像)
- pneu_images = np.array(pneu_images) pneu_images、pneu_labelsを返す
上記の 2 つの関数を宣言したら、それを使用してトレーニング データをロードできるようになります。以下のコードを実行すると、このプロジェクトで tqdm モジュールを実装することにした理由もわかります。 - norm_images、norm_labels = load_normal( '/kaggle/input/chest-xray-pneumonia/chest_xray/train/NORMAL/' ) pneu_images、pneu_labels = load_pneumonia( '/kaggle/input/chest-xray-pneumonia/chest_xray/train/PNEUMONIA/' )
これまでに、norm_images、norm_labels、pneu_images、pneu_labels といういくつかの配列を取得しました。 _images サフィックスが付いた配列は前処理済みの画像が含まれていることを意味し、_labels サフィックスが付いた配列はすべての基本情報 (ラベルとも呼ばれます) が格納されていることを意味します。つまり、 norm_images と pneu_images の両方が X データになり、残りが y データになります。 プロジェクトをシンプルにするために、これらの配列の値を連結し、X_train 配列と y_train 配列に格納しました。 - X_train = np.append(norm_images、pneu_images、軸=0)
- y_train = np.append(norm_labels, pneu_labels)
ちなみに、各クラスの画像数を取得するには、次のコードを使用します。 複数の画像を表示するさて、この段階では、複数の画像を表示することは必須ではありません。しかし、私がやりたいのは、画像が読み込まれ、前処理されているかどうかを確認することです。次のコードは、X_train 配列からランダムに取得された 14 枚の画像をラベルとともに表示するために使用されます。 - 図、軸 = plt.subplots(ncols=7、nrows=2、図サイズ=(16、4))
- インデックス = np.random.choice(len(X_train), 14)
- カウンター = 0
- iが範囲(2)内にある場合:
- jが範囲内(7)の場合:
- axes[i,j].set_title(y_train[indices[counter]]) axes[i,j].imshow(X_train[indices[counter]], cmap= 'gray' )
- axes[i,j].get_xaxis().set_visible( False ) axes[i,j].get_yaxis().set_visible( False ) カウンタ += 1
- plt.show()
上記を見ると、この投稿のカバー画像に使用した画像とは異なり、すべての画像がまったく同じサイズになっていることがわかります。 テストイメージの読み込みすべてのトレーニング データが正常に読み込まれたことがわかったので、まったく同じ関数を使用してテスト データを読み込むことができます。手順はほぼ同じですが、ここでは読み込んだデータを X_test 配列と y_test 配列に保存します。テストに使用されるデータ自体には 624 個のサンプルが含まれています。 - norm_images_test、norm_labels_test = load_normal( '/kaggle/input/chest-xray-pneumonia/chest_xray/test/NORMAL/' )pneu_images_test、pneu_labels_test = load_pneumonia( '/kaggle/input/chest-xray-pneumonia/chest_xray/test/PNEUMONIA/' )X_test = np.append(norm_images_test、pneu_images_test、axis=0)
- y_test = np.append(norm_labels_test、pneu_labels_test) です。
さらに、データセット全体をロードするだけでも非常に長い時間がかかることに気付きました。そのため、pickle モジュールを使用して、X_train、X_test、y_train、y_test を別々のファイルに保存します。こうすることで、次回このデータを使用したいときに、コードを再度実行する必要がなくなります。 - #変数を保存するにはこれを使用します
- と ' pneumonia_data.pickle' 、 'wb' )をf:として開きます。
- pickle.dump((X_train, X_test, y_train, y_test), f) # これを使って 変数をロードする
- と ' pneumonia_data.pickle' 、 'rb' )をf:として開きます。
- (X_train、X_test、y_train、y_test) = pickle.load (f) です。
すべての X データは適切に前処理されているので、今度はラベル y_train と y_test を使用します。 ラベルの前処理この時点で、両方の y 変数は、文字列データ型で記述された正常、細菌、またはウイルスで構成されます。実際、そのようなラベルはニューラル ネットワークにはまったく受け入れられません。したがって、単一の形式に変換する必要があります。 幸いなことに、Scikit-Learn モジュールの OneHotEncoder オブジェクトがあり、これは変換を完了するのに非常に役立ちます。これを行うには、まず y_train と y_test に新しい軸を作成する必要があります。 (OneHotEncoder が期待する形状であるため、この新しい軸を作成します)。 - y_train = y_train[:, np.newaxis]
- y_test = y_test[:, np.newaxis]
次に、one_hot_encoder を次のように初期化します。ここでは、次のステップを簡略化するために、スパース パラメーターとして False を渡していることに注意してください。ただし、スパース行列を使用する場合は、sparse=True を使用するか、引数を空のままにします。 - one_hot_encoder = OneHotEncoder(sparse= False )
最後に、one_hot_encoder を使用してこれらの y データを one-hot に変換します。エンコードされたラベルは y_train_one_hot と y_test_one_hot に保存されます。これら 2 つの配列は、トレーニングに使用するラベルです。 - y_train_one_hot = one_hot_encoder.fit_transform(y_train)
- y_test_one_hot = one_hot_encoder.transform(y_test)
データXを(なし、200、200、1)に変形するそれでは、X_train と X_test に戻りましょう。これら 2 つの配列の形状がそれぞれ (5216, 200, 200) と (624, 200, 200) であることを知っておくことが重要です。 一見すると、これら 2 つの図形は plt.imshow() 関数を使用して表示できるため、問題ないように見えます。ただし、畳み込み層では入力として 1 つのカラー チャネルが想定されるため、この形状は受け入れられません。 したがって、この画像は本質的にグレースケールなので、畳み込み層によって唯一のカラー チャネルとして認識される 1 次元の新しい軸を追加する必要があります。その実装は私の説明ほど複雑ではありませんが、 - X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], X_train.shape[2], 1)
- X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], X_test.shape[2], 1)
上記のコードを実行した後、X_train と X_test の両方の形状を確認すると、形状はそれぞれ (5216, 200, 200, 1) と (624, 200, 200, 1) であることがわかります。 データ拡張データ拡張 (より具体的にはトレーニング データの拡張) のポイントは、より多くのサンプル (それぞれにランダム性がある) を作成することで、トレーニングに使用するデータの量を増やすことです。このランダム性には、平行移動、回転、拡大縮小、せん断、反転などが含まれる場合があります。 この手法は、ニューラル ネットワーク分類器の過剰適合を軽減するのに役立ちます。言い換えれば、モデルをデータ サンプルに対してより適切に一般化できるようになります。幸いなことに、Keras モジュールからインポートできる ImageDataGenerator オブジェクトが存在するため、実装は非常に簡単です。 - datagen = イメージデータジェネレータ(
- 回転範囲 = 10、
- ズーム範囲 = 0.1、
- 幅シフト範囲 = 0.1、
- 高さシフト範囲 = 0.1)
したがって、上記のコードで行っているのは、基本的にランダムな範囲を設定することです。 次に、datagen オブジェクトを初期化した後は、それを X_train と一致させるだけです。次に、このプロセスに flow() メソッドを適用します。このメソッドは、train_gen オブジェクトが拡張データのバッチを生成できるようになるため、非常に便利です。 - datagen.fit(X_train)train_gen = datagen.flow(X_train、y_train_one_hot、batch_size=32) です。
CNN(畳み込みニューラルネットワーク)次は、実際にニューラル ネットワーク アーキテクチャを構築する時です。入力層 (input1) から始めましょう。したがって、このレイヤーは基本的に X データ内のすべての画像サンプルを取得します。したがって、最初のレイヤーが画像サイズとまったく同じ形状を受け入れるようにする必要があります。 (サンプル、幅、高さ、チャネル) ではなく、(幅、高さ、チャネル) のみを定義する必要があることに注意してください。 その後、この入力層はいくつかの畳み込みプーリング層のペアに接続され、最終的に完全接続層に接続されます。 ReLU はシグモイドよりも計算が速いため、モデル内のすべての隠し層は ReLU 活性化関数を使用し、トレーニング時間が短くなることに注意してください。最後に接続される最後のレイヤーは output1 で、これはソフトマックス活性化関数を持つ 3 つのニューロンで構成されます。 ここで Softmax が使用されるのは、出力を各カテゴリの確率値にしたいためです。 - 入力1 = 入力(形状=(X_train.shape[1], X_train.shape[2], 1))
- cnn = Conv2D(16, (3, 3), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(入力1)
- cnn = Conv2D(32, (3, 3), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = MaxPool2D((2, 2))(cnn)
- cnn = Conv2D(16, (2, 2), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = Conv2D(32, (2, 2), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = MaxPool2D((2, 2))(cnn)
- cnn = Flatten()(cnn)cnn = Dense(100, activation= 'relu' )(cnn)
- cnn = Dense(50, アクティベーション= 'relu' )(cnn)
- 出力1 = Dense(3, 活性化= 'softmax' )(cnn)
- モデル = モデル(入力 = input1、出力 = output1)
上記のコードを使用してニューラル ネットワークを構築した後、モデル オブジェクトに summary() を適用してモデルの概要を表示できます。以下は CNN モデルの詳細です。合計で 800 万個のパラメータがあることがわかります。これは確かに多い数です。まあ、それが私がこのコードを Kaggle Notebook で実行している理由です。 要約すると、モデルを構築した後、カテゴリクロスエントロピー損失関数と Adam オプティマイザーを使用してニューラル ネットワークをコンパイルする必要があります。この損失関数は、マルチクラス分類タスクで唯一一般的に使用される関数であるため使用されます。また、ほとんどのニューラル ネットワーク タスクで損失を最小限に抑えるには Adam が最適な選択肢であるため、オプティマイザーとして Adam を選択しました。 - model.compile(loss= 'categorical_crossentropy' ,
- オプティマイザー = 'adam' 、メトリック = [ 'acc' ])
次はモデルをトレーニングします。ここでは、train_gen オブジェクトからトレーニング データを取得するため、fit() ではなく fit_generator() を使用します。データ拡張セクションに注目すると、train_gen が X_train と y_train_one_hot を使用して作成されていることがわかります。したがって、fit_generator() メソッドで Xy ペアを明示的に定義する必要はありません。 - 履歴 = model.fit_generator(train_gen, エポック=30,
- 検証データ = (X_test、y_test_one_hot))
train_gen の特別な点は、ある程度のランダム性を持つサンプルを使用してトレーニング プロセスが完了することです。したがって、X_train にあるすべてのトレーニング データがニューラル ネットワークに直接供給されるわけではありません。代わりに、これらのサンプルは、ジェネレーターがいくつかのランダムな変換を通じて新しい画像を生成するための基礎として使用されます。 さらに、このジェネレーターは各エポックで異なる画像を生成するため、ニューラル ネットワーク分類器がテスト セット内のサンプルをより適切に一般化するのに非常に役立ちます。以下はトレーニングのプロセスです。 - エポック 1/30
- 163/163 [===============================] - 19 秒 114 ミリ秒/ステップ - 損失: 5.7014 - 精度: 0.6133 - 値損失: 0.7971 - 値精度: 0.7228
- 。
- 。
- 。
- エポック 10/30
- 163/163 [===============================] - 18 秒 111 ミリ秒/ステップ - 損失: 0.5575 - 精度: 0.7650 - 値損失: 0.8788 - 値精度: 0.7308
- 。
- 。
- 。
- エポック 20/30
- 163/163 [===============================] - 17 秒 102 ミリ秒/ステップ - 損失: 0.5267 - 精度: 0.7784 - 値損失: 0.6668 - 値精度: 0.7917
- 。
- 。
- 。
- エポック 30/30
- 163/163 [===============================] - 17 秒 104 ミリ秒/ステップ - 損失: 0.4915 - 精度: 0.7922 - 値損失: 0.7079 - 値精度: 0.8045
トレーニング全体自体は、Kaggle Notebook で約 10 分かかりました。ですので、辛抱強く待ってください。トレーニング後、以下に示すように、精度スコアの向上と損失値の減少をプロットできます。 - plt.figure(図サイズ=(8,6))
- plt.title( '精度スコア' )
- plt.plot(履歴.履歴[ 'acc' ])
- plt.plot(履歴.履歴[ 'val_acc' ])
- plt.legend([ 'acc' , 'val_acc' ])
- plt.show()plt.figure(figsize=(8,6))
- plt.title( '損失値' )
- plt.plot(history.history[ '損失' ])
- plt.plot(history.history[ 'val_loss' ])
- plt.legend([ '損失' , 'val_loss' ])
- plt.show()
上記の 2 つのプロットに基づくと、テスト精度と損失値は 30 エポック中に変動しているものの、モデルのパフォーマンスは継続的に向上していると言えます。 ここで注目すべきもう 1 つの重要な点は、プロジェクトの初期段階で適用したデータ拡張方法により、モデルが過剰適合の影響を受けないことです。ここで、最終反復では、トレーニング データとテスト データの精度がそれぞれ 79% と 80% であったことがわかります。 面白い事実: データ拡張メソッドを実装する前は、トレーニング データで 100% の精度が得られましたが、テスト データでは 64% の精度しか得られませんでした。これは明らかに過剰適合です。したがって、ここでは、トレーニング データを増やすと、テスト精度スコアの向上と過剰適合の削減に非常に効果的であることがはっきりとわかります。 モデル評価それでは、混同行列を使用してテストデータの精度を詳しく見てみましょう。まず、すべての X_test を予測し、その結果を one-hot 形式から実際のクラス ラベルに変換する必要があります。 - 予測 = model.predict(X_test)
- 予測 = one_hot_encoder.inverse_transform(予測)
次に、confusion_matrix() 関数を次のように使用します。 - cm = 混乱行列(y_test、予測)
関数内で使用されるパラメータは (実際の値、予測値) であることに注意することが重要です。混同行列関数の戻り値は、予測された分布を格納する 2 次元配列です。マトリックスを解釈しやすくするために、Seaborn モジュールの heatmap() 関数を使用してマトリックスを表示できます。ちなみに、ここでのクラス名リストの値は、one_hotencoder.categories が返す順序に従って取得されます。 - クラス名 = [ 'バクテリア' , '通常' , 'ウイルス' ]plt.figure(figsize=(8,8))
- plt.title( '混同行列' )
- sns.heatmap(cm, cbar= False 、xticklabels=classnames、yticklabels=classnames、fmt= 'd' 、annot= True 、cmap=plt.cm.Blues)
- plt.xlabel( '予測' )
- plt.ylabel( '実際' )
- plt.show()
上記の混同行列に基づくと、45 枚のウイルス X 線画像が細菌であると予測されたことがわかります。これは、2 種類の肺炎を区別することが難しいためと考えられます。しかし、少なくとも 242 個のサンプルのうち 232 個を正しく分類できたため、このモデルは細菌による肺炎を予測するのには優れていると言えます。 プロジェクト全体はこれで終わりです。お読みいただきありがとうございました。以下は、プロジェクト全体を実行するために必要なすべてのコードです。 - インポートOS
- import cv2import pickle #変数の保存に使用import numpy as npiimport matplotlib.pyplot as pltimport seaborn as snsfrom tqdm import tqdm #進捗バーの表示に使用
- sklearn.preprocessingからOneHotEncoder をインポートします
- sklearn.metricsからconfusing_matrix をインポートします
- keras.modelsからモデルをインポートし、load_model を実行します。
- keras.layersからDense、Input、Conv2D、MaxPool2D、Flatten をインポートします。
- from keras.preprocessing.image import ImageDataGenerator #画像を生成するために使用
- np.ランダムシード(22)
- #最後のスラッシュ定義を忘れずに含めてくださいload_normal(norm_path): norm_files = np.array(os.listdir(norm_path)) norm_labels = np.array([ 'normal' ]*len(norm_files))
- norm_images = [] tqdm(norm_files)内の画像の場合:
- # 画像を読み込むimage = cv2.imread(norm_path + image) # 画像を200x200ピクセルにリサイズする
- 画像 = cv2.resize(画像、dsize=(200,200))
- #変換する グレースケール画像へ= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) norm_images.append(image)
- norm_images = np.array(norm_images) norm_images、norm_labelsを返す
- def load_pneumonia(pneu_path): pneu_files = np.array(os.listdir(pneu_path)) pneu_labels = np.array([pneu_file.split( '_' )[1] for pneu_files in pneu_files])
- pneu_images = [] tqdm(pneu_files)内の画像の場合:
- # 画像を読み込むimage = cv2.imread(pneu_path + image) # 画像を200x200ピクセルにリサイズする
- 画像 = cv2.resize(画像、dsize=(200,200))
- #変換する グレースケール画像へ= cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) pneu_images.append(image)
- pneu_images = np.array(pneu_images) pneu_images、pneu_labelsを返す
- print( '画像を読み込んでいます' )
- #すべての画像は_imagesに保存され、すべてのラベルは_labelsnorm_imagesに保存されます。norm_labels = load_normal( '/kaggle/input/chest-xray-pneumonia/chest_xray/train/NORMAL/' )
- pneu_images、pneu_labels = load_pneumonia( '/kaggle/input/chest-xray-pneumonia/chest_xray/train/PNEUMONIA/' )
- #すべての列車画像をX_trainに格納します。X_train = np.append(norm_images, pneu_images, axis=0)
- #すべての列車ラベルをy_trainに追加しますy_train = np.append(norm_labels, pneu_labels)
- 印刷(X_train.shape)
- 印刷(y_train.shape)
- #各クラスのサンプル数を調べるprint(np.unique ( y_train, return_counts= True ))print( '複数の画像を表示する' )
- 図、軸 = plt.subplots(ncols=7、nrows=2、図サイズ=(16、4))
- インデックス = np.random.choice(len(X_train), 14)
- カウンター = 0
- iが範囲(2)内にある場合:
- jが範囲内(7)の場合:
- axes[i,j].set_title(y_train[indices[counter]]) axes[i,j].imshow(X_train[indices[counter]], cmap= 'gray' )
- axes[i,j].get_xaxis().set_visible( False ) axes[i,j].get_yaxis().set_visible( False ) カウンタ += 1
- plt.show()print( 'テスト画像を読み込んでいます' )
- # トレーニングデータで行ったのとまったく同じことを行いますnorm_images_test, norm_labels_test = load_normal( '/kaggle/input/chest-xray-pneumonia/chest_xray/test/NORMAL/' )
- pneu_images_test、pneu_labels_test = load_pneumonia( '/kaggle/input/chest-xray-pneumonia/chest_xray/test/PNEUMONIA/' )
- X_test = np.append(norm_images_test、pneu_images_test、軸=0)
- y_test = np.append(norm_labels_test、pneu_labels_test) です。
- # 読み込んだ画像を将来使用するためにpickle ファイルに保存します
- と ' pneumonia_data.pickle' 、 'wb' )をf:として開きます。
- pickle.dump((X_train, X_test, y_train, y_test), f) #やり方はこうだ ロードする
- と ' pneumonia_data.pickle' 、 'rb' )をf:として開きます。
- (X_train、X_test、y_train、y_test) = pickle.load (f) です。
- print( 'ラベルの前処理' )
- #新しい軸を作成する すべてのyデータ
- y_train = y_train[:, np.newaxis]
- y_test = y_test[:, np.newaxis]
- # OneHotEncoderオブジェクトを初期化する
- one_hot_encoder = OneHotEncoder(sparse= False )
- #変換する すべてのラベルをワンホットにする
- y_train_one_hot = one_hot_encoder.fit_transform(y_train)
- y_test_one_hot = one_hot_encoder.transform(y_test)
- print( 'Xデータの整形' )
- #データを( サンプル数、高さ、幅、1)で、 1は1つのカラーチャンネルを表します。
- X_train = X_train.reshape(X_train.shape[0], X_train.shape[1], X_train.shape[2], 1)
- X_test = X_test.reshape(X_test.shape[0], X_test.shape[1], X_test.shape[2], 1)
- print( 'データ拡張' )
- # 新しい画像を生成する 多少のランダム性
- datagen = イメージデータジェネレータ(
- 回転範囲 = 10、
- ズーム範囲 = 0.1、
- 幅シフト範囲 = 0.1、
- 高さシフト範囲 = 0.1)
- データ生成.fit(X_train)
- train_gen = datagen.flow(X_train、y_train_one_hot、バッチサイズ=32)
- 印刷( 'CNN' )
- #ニューラルネットワークの入力形状を定義する
- 入力シェイプ = (X_train.shape[1]、X_train.shape[2]、1)
- 印刷(入力形状)
- input1 = 入力(形状=input_shape)
- cnn = Conv2D(16, (3, 3), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(入力1)
- cnn = Conv2D(32, (3, 3), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = MaxPool2D((2, 2))(cnn)
- cnn = Conv2D(16, (2, 2), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = Conv2D(32, (2, 2), アクティベーション= 'relu' 、ストライド=(1, 1),
- パディング = '同じ' )(cnn)
- cnn = MaxPool2D((2, 2))(cnn)
- cnn = フラット化()(cnn)
- cnn = Dense(100, アクティベーション= 'relu' )(cnn)
- cnn = Dense(50, アクティベーション= 'relu' )(cnn)
- 出力1 = Dense(3, 活性化= 'softmax' )(cnn)
- モデル = モデル(入力 = 入力1、出力 = 出力1)
- model.compile(loss= 'categorical_crossentropy' ,
- オプティマイザー = 'adam' 、メトリック = [ 'acc' ])
- #代わりにfit_generator()を使用する fit()はデータを使用するので
- #ジェネレータから取得。ランダム性は変化していることに注意してください
- #各エポック
- 履歴 = model.fit_generator(train_gen, エポック=30,
- 検証データ = (X_test、y_test_one_hot))
- # モデルを保存
- モデルを保存します( 'pneumonia_cnn.h5' )
- print( '精度を表示しています' )
- plt.figure(図サイズ=(8,6))
- plt.title( '精度スコア' )
- plt.plot(履歴.履歴[ 'acc' ])
- plt.plot(履歴.履歴[ 'val_acc' ])
- plt.legend([ 'acc' , 'val_acc' ])
- plt.show()
- print( '損失を表示しています' )
- plt.figure(図サイズ=(8,6))
- plt.title( '損失値' )
- plt.plot(history.history[ '損失' ])
- plt.plot(history.history[ 'val_loss' ])
- plt.legend([ '損失' , 'val_loss' ])
- plt.show()
- # テストデータの予測
- 予測 = model.predict(X_test)
- 印刷(予測)
- 予測 = one_hot_encoder.inverse_transform(予測)
- print( 'モデル評価' )
- 印刷(one_hot_encoder.categories_)
- クラス名 = [ '細菌' 、 '通常' 、 'ウイルス' ]
- # 混同行列を表示
- cm = 混乱行列(y_test、予測)
- plt.figure(図サイズ=(8,8))
- plt.title( '混同行列' )
- sns.heatmap(cm, cbar= False 、xticklabels=classnames、yticklabels=classnames、fmt= 'd' 、annot= True 、cmap=plt.cm.Blues)
- plt.xlabel( '予測' )
- plt.ylabel( '実際' )
- plt.show()
|