[[266327]] 人工知能とオープンソースのハードウェアツールを組み合わせることで、深刻な感染症であるマラリアの診断が改善される可能性があります。 人工知能 (AI) とオープンソースのツール、テクノロジー、フレームワークは、社会を進歩させる強力な組み合わせです。 「健康は財産」というのは少し決まり文句かもしれませんが、それは驚くほど正確です。この記事では、低コストで効果的かつ正確なオープンソースのディープラーニング手法と AI を組み合わせて、致命的な感染症であるマラリアを検出する方法をテストします。 私は医者でも医療研究者でもなく、またその資格も持っていません。ただ、AI を医療研究に応用することに興味があるだけです。この記事では、AI とオープンソース ソリューションがマラリア検出にどのように役立ち、手作業の労力を削減できるかを示したいと思います。 Python と TensorFlow: オープンソースのディープラーニング手法を構築するための素晴らしい組み合わせ Python と TensorFlow などのディープラーニング フレームワークの力により、堅牢でスケーラブルかつ効果的なディープラーニング手法を構築できます。これらのツールは無料でオープンソースであるため、非常に手頃な価格で誰でも簡単に導入して使用できるソリューションを構築できます。さあ始めましょう! プロジェクトの動機マラリアは、マラリア原虫によって引き起こされる致命的な伝染性の蚊媒介疾患であり、主に感染した雌のハマダラカに刺されることによって伝染します。マラリアを引き起こす寄生虫は 5 種類ありますが、ほとんどの症例は、熱帯熱マラリア原虫と三日熱マラリア原虫の 2 種類によって引き起こされます。 この地図は、マラリアが世界、特に熱帯地域でどのように広がっているかを示しているが、このプロジェクトの主な動機は、この病気の性質と致死率だった。 感染した雌の蚊に刺されると、蚊が運ぶ寄生虫が血流に入り、酸素を運ぶ赤血球(RBC)を破壊し始めます。通常、マラリアの発症初期症状はインフルエンザウイルスの症状と似ており、蚊に刺されてから数日または数週間以内に発症するのが一般的です。しかし、これらの致命的な寄生虫は、症状を引き起こさずに最大 1 年間体内に生息することがあり、治療が遅れると合併症や死に至ることもあります。したがって、早期発見は命を救うことができます。 世界保健機関(WHO)のマラリアに関する事実によれば、世界人口のほぼ半数がマラリアの危険にさらされており、毎年2億人以上がマラリアに罹患し、40万人近くが死亡しています。これは、マラリア検査と診断を迅速、シンプル、かつ効果的に行う動機の一つです。 マラリアの検出方法マラリアを検出し診断するために使用できる方法はいくつかあります。この記事のプロジェクトは、ポリメラーゼ連鎖反応 (PCR) や迅速診断テスト (RDT) など、Rajaraman らの論文「薄い血液塗抹標本画像におけるマラリア原虫検出の改善のための特徴抽出器としての事前トレーニング済み畳み込みニューラル ネットワーク」で紹介されているいくつかの方法に基づいています。どちらの検査も、高品質の顕微鏡検査サービスが利用できない場合によく使用されます。 Carlos Ariza 氏の記事「Malaria Hero: マラリア原虫をより迅速に診断するための Web アプリ」によると、標準的なマラリア診断は通常、血液塗抹標本のワークフローに基づいています。この記事から、Adrian Rosebrock 氏の「Keras を使用したディープラーニングと医療画像分析」について学びました。マラリア原虫の予防、診断、治療についてさらに多くのアイデアを与えてくれたこれらの優れたリソースの著者に感謝します。 マラリア原虫検出のための血液塗抹標本のワークフロー WHO のプロトコルによれば、診断には通常、100 倍に拡大した血液塗抹標本の集中検査が含まれます。訓練を受けた人々が、5,000個の赤血球のうち何個にマラリア原虫が含まれているかを手作業で数えた。上記のRajaramanらによる論文で説明されているように: 厚い血液塗抹標本は寄生虫の存在を検出するのに役立ち、薄い血液塗抹標本は感染の原因となる寄生虫の種類を特定するのに役立ちます (米国疾病管理予防センター、2012 年)。診断の正確さは、診断を行う人の専門知識に大きく依存し、観察者間のばらつきや、病気が流行している地域や資源が限られた地域での大規模な診断によって悪影響を受ける可能性があります (Mitiku、Mengistu、Gelaw、2003)。代替技術としては、ポリメラーゼ連鎖反応(PCR)と迅速診断検査(RDT)が用いられるが、PCR分析は性能に限界があり(Hommelsheimら、2014年)、RDTは病気が流行している地域では費用対効果が高くない(Hawkes、Katsuva、Masumbuko、2009年)。
したがって、マラリア検出は機械学習を使用した自動化の恩恵を受ける可能性があります。 マラリア検出のためのディープラーニング血液塗抹標本の手動診断は、寄生虫に感染した細胞と感染していない細胞を分類して数える専門知識を必要とする、手間のかかる手作業です。このプロセスは、特に専門の人員が不足している地域では、うまく拡張できない可能性があります。最先端の画像処理および分析技術を使用して手動で選択された特徴を抽出し、機械学習ベースの分類モデルを構築する上で、ある程度の進歩がありました。ただし、これらのモデルは、トレーニングするためのデータがなくなり、手動で特徴を選択するのに長い時間がかかるため、大規模に一般化することはできません。 ディープラーニング モデル、より具体的には畳み込みニューラル ネットワーク (CNN) は、さまざまなコンピューター ビジョン タスクで非常に効果的であることが証明されています。 (CNN の背景をもっと知りたい場合は、CS2331n 視覚認識のための畳み込みニューラル ネットワークを読むことをお勧めします。) 簡単に言うと、CNN モデルの主要なレイヤーには、次の図に示すように、畳み込みレイヤーとプーリング レイヤーが含まれます。 典型的なCNNアーキテクチャ 畳み込み層は、変換不変のデータから空間階層パターンを学習するため、画像のさまざまな側面を学習できます。たとえば、最初の畳み込み層はエッジやコーナーなどの小さくて局所的なパターンを学習し、2 番目の畳み込み層は最初の層の特徴に基づいてより大きなパターンを学習します。これにより、CNN は自動的に特徴を抽出し、新しいデータ ポイントに一般化できる効果的な特徴を学習できるようになります。プーリング レイヤーは、ダウンサンプリングと次元の削減に役立ちます。 したがって、CNN は特徴エンジニアリングの自動化と拡張に役立ちます。同様に、モデルの最後に密なレイヤーを追加すると、画像分類などのタスクを実行できるようになります。 CNN のようなディープラーニング モデルを使用したマラリア自動検出は、特に転移学習と事前トレーニング済みモデルが少量のデータでも非常にうまく機能するため、非常に効果的で、安価で、スケーラブルです。 Rajaraman らによる論文では、6 つの事前トレーニング済みモデルを使用して、単一のデータセットでマラリアと非感染サンプルの検出において驚異的な 95.9% の精度を達成しました。私たちの焦点は、いくつかのシンプルな CNN モデルを最初から試し、事前トレーニング済みのモデルで転移学習を使用して、同じデータセットから何が得られるかを確認することです。モデルの構築には、Python や TensorFlow などのオープンソース ツールとフレームワークを使用します。 データセット私たちが分析したデータは、国立医学図書館(NLM)の一部であるリスターヒル国立生物医学コミュニケーションセンター(LHNCBC)の研究者から提供されたもので、彼らは健康な血液塗抹標本画像と感染した血液塗抹標本画像の公開データセットを注意深く収集し、ラベル付けしました。研究者らは、Androidスマートフォンで動作し、従来の光学顕微鏡に接続するマラリア検出モバイルアプリを開発した。研究チームは、バングラデシュのチッタゴン医科大学病院で採取され、写真撮影された熱帯熱マラリア原虫に感染した150人の患者と健康な患者50人の血液薄層塗抹標本をギムザ染色で染色した。スマートフォンの内蔵カメラを使用して、各顕微鏡ビューポートの画像をキャプチャします。これらの画像は、タイのバンコクにあるマヒドン・オックスフォード熱帯医学研究所の専門家がスライドリーダーを使用してラベル付けした。 データセットの構造を簡単に確認してみましょう。まず、いくつかの基本的な依存関係をインストールします (使用するオペレーティング システムに基づきます)。 依存関係のインストール モデルをより高速に実行できるように、GPU を搭載したクラウド上で Debian ベースの OS を使用しています。ディレクトリ構造を確認するには、 sudo apt install tree を使用して、 tree とその依存関係 (インストールされていない場合) をインストールする必要があります。 ツリー依存関係のインストール 感染した血液細胞と健康な血液細胞の画像を含む 2 つのフォルダーがあります。次のように入力すると、画像の合計数に関する詳細情報を取得できます。 インポートOS インポートグロブ
base_dir = os.path.join('./cell_images') 感染したディレクトリ = os.path.join(base_dir,'寄生') healthy_dir = os.path.join(base_dir,'感染していない')
感染したファイル = glob.glob(感染したディレクトリ+'/*.png') healthy_files = glob.glob(healthy_dir+'/*.png') len(感染ファイル)、len(正常なファイル)
# 出力 (13779, 13779) 13,779 枚のマラリア感染血液細胞画像と 13,779 枚の非マラリア感染 (健康な) 血液細胞画像で構成されたバランスの取れたデータセットがあるようです。これらからデータフレームを構築し、データセットの構築に使用しましょう。 numpyをnpとしてインポートする pandasをpdとしてインポートする
np.ランダムシード(42)
files_df = pd.DataFrame({ 'ファイル名': 感染ファイル + 正常なファイル、 'ラベル': ['マラリア'] * len(感染ファイル数) + ['健康'] * len(健康なファイル数) }).sample(frac=1, random_state=42).reset_index(drop=True)
ファイル_df.head() データセット 画像データセットの構築と理解ディープラーニング モデルを構築するには、トレーニング データが必要ですが、未知のデータを使用してモデルのパフォーマンスをテストする必要もあります。したがって、トレーニング、検証、テスト用のデータセットを 60:10:30 の比率で分割します。トレーニング中にトレーニング データセットと検証データセットを適用し、モデルのパフォーマンスを確認するためにテスト データセットを適用します。 sklearn.model_selection から train_test_split をインポートします コレクションからカウンターをインポート
train_files、test_files、train_labels、test_labels = train_test_split(files_df['filename'].values、 files_df['label'].values、 テストサイズ=0.3、ランダム状態=42) train_files、val_files、train_labels、val_labels = train_test_split(train_files、 トレーニングラベル、 テストサイズ=0.1、ランダム状態=42)
印刷(train_files.shape、val_files.shape、test_files.shape) print('Train:', Counter(train_labels), '\nVal:', Counter(val_labels), '\nTest:', Counter(test_labels))
# 出力 (17361,) (1929,) (8268,) 電車: カウンター({'healthy': 8734, 'malaria': 8627}) 値: Counter({'healthy': 970, 'malaria': 959}) テスト: Counter({'malaria': 4193, 'healthy': 4075}) 血液塗抹標本と細胞画像は人、検査方法、画像の向きによって異なるため、これらの画像は同じサイズではありません。トレーニング データセットの統計をまとめ、最適な画像サイズを決定しましょう (テスト データセットにはまったく触れないことに注意してください)。 cv2をインポート 同時輸入先物から インポートスレッド
get_img_shape_parallel(idx、img、total_imgs)を定義します。 idx % 5000 == 0 または idx == (total_imgs - 1) の場合: print('{}: img num: {} で作業中'.format(threading.current_thread().name, idx)) cv2.imread(img).shapeを返す
ex = futures.ThreadPoolExecutor(max_workers=なし) data_inp = [(idx, img, len(train_files)) idx、img は enumerate(train_files)] print('Img シェイプの計算を開始しています:') train_img_dims_map = ex.map(get_img_shape_parallel, [data_inpのレコードのレコード[0]]、 [data_inpのレコードのレコード[1]]、 [data_inpのレコードのレコード[2]]) train_img_dims = リスト(train_img_dims_map) print('最小寸法:', np.min(train_img_dims, axis=0)) print('平均寸法:', np.mean(train_img_dims, axis=0)) print('中央値寸法:', np.median(train_img_dims, axis=0)) print('最大寸法:', np.max(train_img_dims, axis=0))
# 出力 Img シェイプの計算を開始します: ThreadPoolExecutor-0_0: 画像番号 0 で作業中 ThreadPoolExecutor-0_17: 画像番号 5000 で作業中 ThreadPoolExecutor-0_15: 画像番号 10000 で作業中 ThreadPoolExecutor-0_1: 画像番号 15000 で作業中 ThreadPoolExecutor-0_7: 画像番号 17360 で作業中 最小寸法: [46 46 3] 平均寸法: [132.77311215 132.45757733 3.] 平均寸法: [130. 130. 3.] 最大寸法: [385 394 3] 画像の読み取りを高速化するために並列処理を適用し、要約統計に基づいて各画像のサイズを 125 x 125 ピクセルに変更します。すべての画像を読み込み、これらの固定サイズにサイズ変更してみましょう。 IMG_DIMS = (125, 125)
get_img_data_parallel(idx、img、total_imgs)を定義します。 idx % 5000 == 0 または idx == (total_imgs - 1) の場合: print('{}: img num: {} で作業中'.format(threading.current_thread().name, idx)) 画像 = cv2.imread(画像) img = cv2.resize(img, dsize=IMG_DIMS, 補間=cv2.INTER_CUBIC) img = np.array(img、dtype=np.float32) です。 画像を返す
例: futures.ThreadPoolExecutor(max_workers=なし) train_data_inp = [(idx, img, len(train_files)) idx、img が enumerate(train_files)] の場合 val_data_inp = [(idx, img, len(val_files)) idx、img は enumerate(val_files)] test_data_inp = [(idx, img, len(test_files)) idx、img が enumerate(test_files)] の場合
print('列車の画像を読み込んでいます:') train_data_map = ex.map(get_img_data_parallel, [train_data_inpのレコードのレコード[0]]、 [train_data_inpのレコードのレコード[1]]、 [train_data_inpのレコードのレコード[2]]) train_data = np.array(リスト(train_data_map))
print('\n検証画像を読み込んでいます:') val_data_map = ex.map(get_img_data_parallel, [val_data_inpのレコードのレコード[0]]、 [val_data_inpのレコードのレコード[1]]、 [val_data_inpのレコードのレコード[2]]) val_data = np.array(リスト(val_data_map))
print('\nテスト画像を読み込んでいます:') test_data_map = ex.map(get_img_data_parallel, [test_data_inpのレコードのレコード[0]]、 [test_data_inpのレコードのレコード[1]]、 [test_data_inpのレコードのレコード[2]]) test_data = np.array(リスト(test_data_map))
トレーニングデータ.shape、val_data.shape、テストデータ.shape
# 出力 列車の画像を読み込んでいます: ThreadPoolExecutor-1_0: 画像番号 0 で作業中 ThreadPoolExecutor-1_12: 画像番号 5000 で作業中 ThreadPoolExecutor-1_6: 画像番号 10000 で作業中 ThreadPoolExecutor-1_10: 画像番号 15000 で作業中 ThreadPoolExecutor-1_3: 画像番号 17360 で作業中
検証画像を読み込んでいます: ThreadPoolExecutor-1_13: 画像番号 0 で作業中 ThreadPoolExecutor-1_18: 画像番号 1928 で作業中
テスト画像を読み込んでいます: ThreadPoolExecutor-1_5: 画像番号 0 で作業中 ThreadPoolExecutor-1_19: 画像番号 5000 で作業中 ThreadPoolExecutor-1_8: 画像番号 8267 で作業中 ((17361, 125, 125, 3)、(1929, 125, 125, 3)、(8268, 125, 125, 3)) 画像の読み込みとサイズ変更に伴う計算を高速化するために、再び並列処理を適用します。最後に、前の出力に示すように、目的のサイズの画像テンソルを取得します。データの概要を把握するために、いくつかのサンプルの血球画像を見てみましょう。 matplotlib.pyplot を plt としてインポートします。 %matplotlib インライン
plt.figure(1, 図のサイズ = (8, 8)) 0 の場合 iが範囲(16)内にある場合: 1 + = 1 r = np.random.randint(0, train_data.shape[0], 1) です。 plt.subplot(4, 4, n) サブプロット plt.subplots_adjust(縦スペース = 0.5、横スペース = 0.5) plt.imshow(train_data[r[0]]/255.) plt.title('{}'.format(train_labels[r[0]])) plt.xticks([]) 、plt.yticks([]) マラリア細胞サンプル これらのサンプル画像を見ると、マラリア細胞と健康な細胞の画像の間に微妙な違いがあることがわかります。モデルのトレーニング中に、ディープラーニング モデルにこれらのパターンを学習させます。 モデルのトレーニングを開始する前に、いくつかの基本的な構成設定を確立する必要があります。 バッチサイズ = 64 クラス数 = 2 エポック = 25 入力シェイプ = (125, 125, 3)
train_imgs_scaled = train_data / 255 です。 val_imgs_scaled = val_data / 255 です。
# テキストカテゴリラベルをエンコードする sklearn.preprocessing から LabelEncoder をインポートします
le = ラベルエンコーダ() le.fit(ラベルのトレーニング) train_labels_enc = le.transform(train_labels) val_labels_enc = le.transform(val_labels)
印刷(train_labels[:6]、train_labels_enc[:6])
# 出力 ['マラリア' 'マラリア' 'マラリア' '健康' '健康' 'マラリア'] [1 1 1 0 0 1] 画像の寸法、バッチ サイズ、エポックを固定し、分類のクラス ラベルをエンコードします。 TensorFlow 2.0 は 2019 年 3 月にリリースされましたが、この演習はそれを試してみるのに最適な理由です。 テンソルフローをtfとしてインポートする
# TensorBoardノートブック拡張機能をロードする(オプション) %load_ext テンソルボード.ノートブック
tf.ランダム.set_seed(42) tf.__バージョン__
# 出力 '2.0.0-アルファ0' ディープラーニングトレーニングモデルトレーニングフェーズでは、3 つのディープトレーニングモデルを構築し、トレーニングセットを使用してトレーニングし、検証データを使用してパフォーマンスを比較します。これらのモデルを保存し、後でモデル評価フェーズで使用します。 モデル 1: ゼロからの CNN私たちの最初のマラリア検出モデルは、基本的な CNN を使用してゼロから構築およびトレーニングされます。まず、モデルアーキテクチャを定義しましょう。 inp = tf.keras.layers.Input(shape=INPUT_SHAPE)
conv1 = tf.keras.layers.Conv2D(32, カーネルサイズ=(3, 3), アクティベーション='relu'、パディング='same')(inp) プール1 = tf.keras.layers.MaxPooling2D(プールサイズ=(2, 2))(conv1) conv2 = tf.keras.layers.Conv2D(64, カーネルサイズ=(3, 3), アクティベーション='relu'、パディング='same')(プール1) プール2 = tf.keras.layers.MaxPooling2D(プールサイズ=(2, 2))(conv2) conv3 = tf.keras.layers.Conv2D(128, カーネルサイズ=(3, 3), アクティベーション='relu'、パディング='same')(プール2) プール3 = tf.keras.layers.MaxPooling2D(プールサイズ=(2, 2))(conv3)
フラット = tf.keras.layers.Flatten()(pool3)
hidden1 = tf.keras.layers.Dense(512, activation='relu')(フラット) drop1 = tf.keras.layers.Dropout(レート=0.3)(hidden1) hidden2 = tf.keras.layers.Dense(512, activation='relu')(drop1) drop2 = tf.keras.layers.Dropout(レート=0.3)(hidden2)
出力 = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
モデル = tf.keras.Model(入力 = inp、出力 = out) model.compile(optimizer='adam', 損失='バイナリクロスエントロピー'、 メトリック=['精度']) モデル.要約()
# 出力 モデル: 「モデル」 _________________________________________________________________ レイヤー(タイプ)出力形状パラメータ# ================================================================= input_1 (入力レイヤー) [(なし, 125, 125, 3)] 0 _________________________________________________________________ conv2d (Conv2D) (なし、125、125、32) 896 _________________________________________________________________ max_pooling2d (MaxPooling2D) (なし、62、62、32) 0 _________________________________________________________________ conv2d_1 (Conv2D) (なし、62、62、64) 18496 _________________________________________________________________ ... ... _________________________________________________________________ 密_1 (密) (なし、512) 262656 _________________________________________________________________ dropout_1 (ドロップアウト) (なし、512) 0 _________________________________________________________________ 密_2 (密) (なし、1) 513 ================================================================= 合計パラメータ: 15,102,529 トレーニング可能なパラメータ: 15,102,529 トレーニング不可能なパラメータ: 0 _________________________________________________________________ これらのコードのアーキテクチャに基づいて、CNN モデルには 3 つの畳み込み層と 1 つのプーリング層があり、その後に 2 つの密な層と正規化のためのドロップアウトが続きます。モデルをトレーニングしましょう。 日時をインポート
logdir = os.path.join('/home/dipanzan_sarkar/projects/tensorboard_logs', datetime.datetime.now().strftime("%Y%m%d-%H%M%S")) tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir、histogram_freq=1) tf.keras.callbacks.ReduceLROnPlateau(モニター='val_loss'、係数=0.5、 忍耐力=2、最小lr=0.000001) コールバック = [reduce_lr, tensorboard_callback]
履歴 = model.fit(x=train_imgs_scaled, y=train_labels_enc, バッチサイズ=BATCH_SIZE、 エポック=EPOCHS、 検証データ=(val_imgs_scaled、val_labels_enc)、 コールバック=コールバック、 詳細=1)
# 出力 17361 個のサンプルでトレーニングし、1929 個のサンプルで検証 エポック 1/25 17361/17361 [====] - 32秒 2ミリ秒/サンプル - 損失: 0.4373 - 精度: 0.7814 - val_loss: 0.1834 - val_accuracy: 0.9393 エポック 2/25 17361/17361 [====] - 30秒 2ms/サンプル - 損失: 0.1725 - 精度: 0.9434 - val_loss: 0.1567 - val_accuracy: 0.9513 ... ... エポック24/25 17361/17361 [====] - 30秒 2ms/サンプル - 損失: 0.0036 - 精度: 0.9993 - val_loss: 0.3693 - val_accuracy: 0.9565 エポック 25/25 17361/17361 [====] - 30秒 2ms/サンプル - 損失: 0.0034 - 精度: 0.9994 - val_loss: 0.3699 - val_accuracy: 0.9559 検証精度は 95.6% を達成しました。これはかなり良い結果ですが、モデルは少し過剰適合しているように見えます (トレーニング精度は 99.9% です)。トレーニングと検証の精度と損失の曲線をプロットすると、これが明確にわかります。 f, (ax1, ax2) = plt.subplots(1, 2, 図のサイズ=(12, 4)) t = f.suptitle('基本的なCNNパフォーマンス', フォントサイズ=12) f.subplots_adjust(トップ=0.85、幅=0.3)
max_epoch = len(history.history['accuracy'])+1 epoch_list = リスト(範囲(1,最大epoch)) ax1.plot(epoch_list, history.history['accuracy'], label='トレーニング精度') ax1.plot(epoch_list, history.history['val_accuracy'], label='検証精度') ax1.set_xticks(np.arange(1, max_epoch, 5)) ax1.set_ylabel('精度値') ax1.set_xlabel('エポック') ax1.set_title('精度') l1 = ax1.legend(loc="best")
ax2.plot(epoch_list, history.history['loss'], label='列車損失') ax2.plot(epoch_list, history.history['val_loss'], label='検証損失') ax2.set_xticks(np.arange(1, max_epoch, 5)) ax2.set_ylabel('損失値') ax2.set_xlabel('エポック') ax2.set_title('損失') l2 = ax2.legend(loc="best") 基本的なCNNの学習曲線 第 5 の時代になっても、状況はあまり改善されていないことがわかります。このモデルを将来の評価のために保存しましょう。 モデルを保存します('basic_cnn.h5') ディープラーニング人間には異なるタスク間で知識を転送する生来の能力があるのと同様に、転移学習により、機械学習やディープラーニングのコンテキストでも、以前のタスクから学習した知識を新しい関連タスクに活用できるようになります。転移学習についてさらに詳しく知りたい場合は、私の記事「実際のアプリケーションを使用したディープラーニングにおける転移学習の実践ガイド」と著書「Python での実践的な転移学習」をお読みください。 ディープラーニングの考え方 この実践で探求したいアイデアは次のとおりです。 私たちの問題の文脈では、事前トレーニング済みのディープラーニング モデル (ImageNet のような大規模なデータセットでトレーニング済み) を活用して、知識を適用および転送することでマラリア検出の問題を解決できるでしょうか?
最も人気のある 2 つのディープ トランスファー ラーニング戦略を適用します。 - 特徴抽出器としての事前トレーニング済みモデル
- 事前学習済みモデルの微調整
私たちの実験には、ケンブリッジ大学の Visual Geometry Group (VGG) によって開発された、事前トレーニング済みの VGG-19 ディープ ニューラル ネットワーク モデルを使用します。 VGG-19 のような事前トレーニング済みモデルは、さまざまな画像分類を使用して大規模なデータセット (Imagenet) でトレーニングされます。したがって、このモデルは、CNN モデルによって学習された特徴に対して空間的、回転的、および並進的に不変な堅牢な特徴階層を学習しているはずです。したがって、何百万もの画像から優れた特徴セットを学習したこのモデルは、マラリア検出などのコンピューター ビジョンの問題に対する新しい画像の優れた特徴抽出器として使用できます。転移学習の力を問題に適用する前に、まず VGG-19 モデルについて説明しましょう。 VGG-19モデルの理解VGG-19 モデルは、画像認識と分類を目的として開発された ImageNet データベース上に構築された 19 層 (畳み込みおよび完全接続) のディープラーニング ネットワークです。このモデルは Karen Simonyan 氏と Andrew Zisserman 氏によって構築され、論文「大規模画像認識のための非常に深い畳み込みネットワーク」で説明されています。 VGG-19 のアーキテクチャ モデルは次のとおりです。 VGG-19 モデルアーキテクチャ 3x3 畳み込みフィルターを持つ合計 16 個の畳み込み層、ダウンサンプリング用の最大プーリング層、4096 ユニットの完全接続された 2 つの隠し層があり、それぞれの隠し層に ImageNet データベースの各クラスに 1 つずつ、1000 ユニットの密な層が続いていることがわかります。マラリア予測には独自の完全に接続された密なレイヤーを使用するため、最後の 3 つのレイヤーは必要ありません。私たちは最初の 5 つのブロックに重点を置いているため、VGG モデルを効果的な特徴抽出器として利用できます。 5 つの畳み込みブロックをフリーズして、各エポック後にビット重みが更新されないようにすることで、モデルの 1 つを単純な特徴抽出器として使用します。最後のモデルでは、VGG モデルを微調整し、最後の 2 つのブロック (4 番目と 5 番目) を解凍して、モデルをトレーニングするときに各エポック (データの各バッチ) で重みが更新されるようにします。 モデル2: 特徴抽出器としての事前学習済みモデルこのモデルを構築するには、TensorFlow を使用して VGG-19 モデルを読み込み、畳み込みブロックをフリーズして、特徴抽出器として使用できるようにします。最後に、分類タスクを実行するために独自の高密度レイヤーを挿入します。 vgg = tf.keras.applications.vgg19.VGG19(include_top=False、重み='imagenet'、 入力シェイプ=INPUT_SHAPE) vgg.trainable = False # レイヤーをフリーズする vgg.layers のレイヤーの場合: レイヤー.trainable = False
ベース_vgg = vgg base_out = base_vgg.出力 pool_out = tf.keras.layers.Flatten()(base_out) hidden1 = tf.keras.layers.Dense(512, activation='relu')(pool_out) drop1 = tf.keras.layers.Dropout(レート=0.3)(hidden1) hidden2 = tf.keras.layers.Dense(512, activation='relu')(drop1) drop2 = tf.keras.layers.Dropout(レート=0.3)(hidden2)
出力 = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
モデル = tf.keras.Model(入力 = base_vgg.input、出力 = out) モデルをコンパイルします(オプティマイザー=tf.keras.optimizers.RMSprop(lr=1e-4)、 損失='バイナリクロスエントロピー'、 メトリック=['精度']) モデル.要約()
# 出力 モデル: "model_1" _________________________________________________________________ レイヤー(タイプ)出力形状パラメータ# ================================================================= input_2 (入力レイヤー) [(なし, 125, 125, 3)] 0 _________________________________________________________________ block1_conv1 (Conv2D) (なし、125、125、64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (なし、125、125、64) 36928 _________________________________________________________________ ... ... _________________________________________________________________ block5_pool (MaxPooling2D) (なし、3、3、512) 0 _________________________________________________________________ flatten_1 (フラット化) (なし、4608) 0 _________________________________________________________________ 密_3 (密) (なし、512) 2359808 _________________________________________________________________ dropout_2 (ドロップアウト) (なし、512) 0 _________________________________________________________________ 密_4 (密) (なし、512) 262656 _________________________________________________________________ dropout_3 (ドロップアウト) (なし、512) 0 _________________________________________________________________ 密_5 (密) (なし、1) 513 ================================================================= 合計パラメータ: 22,647,361 トレーニング可能なパラメータ: 2,622,977 トレーニング不可能なパラメータ: 20,024,384 _________________________________________________________________ 出力全体から明らかなように、モデルには多くのレイヤーがあり、特徴抽出器としては VGG-19 モデルの固定レイヤーのみを利用します。次のコードを使用すると、モデルの何層が実際にトレーニング可能か、ネットワーク内に合計でいくつの層があるかを確認できます。 print("レイヤーの合計数:", len(model.layers)) print("トレーニング可能なレイヤーの合計数:", sum([model.layers 内の l が l.trainable の場合 1]))
# 出力 合計レイヤー数: 28 トレーニング可能なレイヤーの合計数: 6 以前のモデルと同様の構成とコールバックを使用してモデルをトレーニングします。モデルをトレーニングするための完全なコードについては、私の GitHub リポジトリを参照してください。モデルの精度と損失曲線を示す次のグラフを見てみましょう。 凍結された事前トレーニング済み CNN の学習曲線 これは、モデルがベース CNN モデルのように過剰適合していないが、パフォーマンスがベース CNN モデルよりも少し劣っていることを示しています。このモデルを将来の評価のために保存しましょう。 モデルを保存します('vgg_frozen.h5') モデル3: 画像拡張を使用して事前学習済みモデルを微調整する最終モデルでは、事前定義された VGG-19 モデルの最後の 2 つのブロックのレイヤーの重みを微調整します。画像強化の概念も紹介します。画像強化の背後にある考え方は、その名前が示すとおりです。トレーニング データセットから既存の画像を読み込み、回転、切り取り、変換、スケーリングなどの変換を適用して、新しい変更されたバージョンを作成します。これらのランダムな変換により、得られる画像は毎回異なります。画像拡張ツールの構築には、tf.keras の ImageDataGenerator という優れたツールを使用します。 train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255, ズーム範囲=0.05、 回転範囲=25、 幅シフト範囲=0.05、 高さシフト範囲=0.05、 shear_range=0.05、horizontal_flip=True、 fill_mode = 'nearest')
val_datagen = tf.keras.preprocessing.image.ImageDataGenerator(再スケール = 1./255)
# 画像拡張ジェネレータを構築する train_generator = train_datagen.flow(train_data、train_labels_enc、batch_size=BATCH_SIZE、shuffle=True) val_generator = val_datagen.flow(val_data、val_labels_enc、batch_size=BATCH_SIZE、shuffle=False) 各エポックでモデルのパフォーマンスを評価するために検証データセットを使用するため、検証データセットには変換を適用しません (必要なサイズ変更を除く)。転移学習における画像拡張の詳細な説明については、上で参照した論文を参照してください。一連の画像拡張変換からのサンプル結果を見てみましょう。 画像ID = 0 sample_generator = train_datagen.flow(train_data[img_id:img_id+1], train_labels[img_id:img_id+1], バッチサイズ=1) サンプル = [next(sample_generator) i が範囲(0,5)] 図、ax = plt.subplots(1,5、図サイズ=(16, 6)) print('ラベル:', [サンプル内のアイテムのitem[1][0]]) l = [ax[i].imshow(sample[i][0][0])、iが範囲(0,5)] 拡張画像のサンプル 前回の出力から画像にわずかな変化がはっきりと見られます。ここで、学習モデルを構築し、VGG-19 モデルの最後の 2 つのブロックがトレーニング可能であることを確認します。 vgg = tf.keras.applications.vgg19.VGG19(include_top=False、重み='imagenet'、 入力シェイプ=INPUT_SHAPE) # レイヤーをフリーズする vgg.trainable = True
set_trainable = False vgg.layers のレイヤーの場合: レイヤー名が['block5_conv1', 'block4_conv1']内にある場合: set_trainable = True set_trainableの場合: レイヤー.trainable = True それ以外: レイヤー.trainable = False
ベース_vgg = vgg base_out = base_vgg.出力 pool_out = tf.keras.layers.Flatten()(base_out) hidden1 = tf.keras.layers.Dense(512, activation='relu')(pool_out) drop1 = tf.keras.layers.Dropout(レート=0.3)(hidden1) hidden2 = tf.keras.layers.Dense(512, activation='relu')(drop1) drop2 = tf.keras.layers.Dropout(レート=0.3)(hidden2)
出力 = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
モデル = tf.keras.Model(入力 = base_vgg.input、出力 = out) モデルをコンパイルします(オプティマイザー=tf.keras.optimizers.RMSprop(lr=1e-5)、 損失='バイナリクロスエントロピー'、 メトリック=['精度'])
print("レイヤーの合計数:", len(model.layers)) print("トレーニング可能なレイヤーの合計数:", sum([1 for l in model.layers if l.trainable]))
# 出力 合計レイヤー数: 28 トレーニング可能なレイヤーの合計数: 16 微調整時に事前トレーニング済みのレイヤーに大きな重みの更新を加えたくないため、モデルの学習率を下げました。データジェネレータを使用しているため、モデルのトレーニングプロセスが若干異なる可能性があります。そのため、 fit_generator(...) 関数を適用します。 tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir、histogram_freq=1) tf.keras.callbacks.ReduceLROnPlateau(モニター='val_loss'、係数=0.5、 忍耐力=2、最小lr=0.000001)
コールバック = [reduce_lr, tensorboard_callback] train_steps_per_epoch = train_generator.n // train_generator.batch_size val_steps_per_epoch = val_generator.n // val_generator.batch_size history = model.fit_generator(train_generator, ステップ数/エポック数=train_steps_per_epoch, エポック数=EPOCHS, 検証データ=val_generator、検証ステップ=val_steps_per_epoch、 詳細=1)
# 出力 エポック 1/25 271/271 [====] - 133秒 489ミリ秒/ステップ - 損失: 0.2267 - 精度: 0.9117 - val_loss: 0.1414 - val_accuracy: 0.9531 エポック 2/25 271/271 [====] - 129秒 475ミリ秒/ステップ - 損失: 0.1399 - 精度: 0.9552 - val_loss: 0.1292 - val_accuracy: 0.9589 ... ... エポック24/25 271/271 [====] - 128秒 473ミリ秒/ステップ - 損失: 0.0815 - 精度: 0.9727 - val_loss: 0.1466 - val_accuracy: 0.9682 エポック 25/25 271/271 [====] - 128秒 473ミリ秒/ステップ - 損失: 0.0792 - 精度: 0.9729 - val_loss: 0.1127 - val_accuracy: 0.9641 これは私たちにとって最適なモデルのようです。検証精度はほぼ 96.5% となり、トレーニング精度に基づくと、最初のモデルのように過剰適合しているようには見えません。これは次の学習曲線によって確認できます。 微調整された事前トレーニング済み CNN の学習曲線 このモデルを保存して、テスト セットで使用できるようにします。 モデルを保存します('vgg_finetuned.h5') これでモデルのトレーニング フェーズは完了です。これで、テスト セットでモデルのパフォーマンスをテストする準備が整いました。 ディープラーニングモデルのパフォーマンス評価検証だけでは不十分なので、テスト セットで予測を行って、トレーニング フェーズで構築した 3 つのモデルを評価します。また、関連する分類メトリックを使用してディープラーニング モデルのパフォーマンスを評価するために使用できる、 model_evaluation_utils というテスト ユーティリティ モジュールも構築しました。最初のステップはデータセットを拡張することです。 test_imgs_scaled = test_data / 255 です。 test_imgs_scaled.shape、test_labels.shape
# 出力 ((8268, 125, 125, 3), (8268,)) 次のステップでは、保存したディープラーニング モデルを読み込み、テスト セットで予測を行います。 # 保存したディープラーニングモデルを読み込む basic_cnn = tf.keras.models.load_model('./basic_cnn.h5') vgg_frz = tf.keras.models.load_model( './ vgg_frozen.h5') vgg_ft = tf.keras.models.load_model( './ vgg_finetuned.h5')
#テストデータを予測します BASIC_CNN_PREDS = BASIC_CNN.PREDICT(test_imgs_scaled、batch_size = 512) vgg_frz_preds = vgg_frz.predict(test_imgs_scaled、batch_size = 512) vgg_ft_preds = vgg_ft.predict(test_imgs_scaled、batch_size = 512)
BASIC_CNN_PRED_LABELS = le.inverse_Transform([1 pred> 0.5 else 0 basic_cnn_preds.ravel()]のpredの場合) vgg_frz_pred_labels = le.inverse_transform([1 pred> 0.5 else 0 vgg_frz_preds.ravel()]でpredの場合) vgg_ft_pred_labels = le.inverse_transform([1 pred> 0.5 else 0 vgg_ft_preds.ravel()]でpredの場合) 次のステップは、 model_evaluation_utils モジュールを適用して、対応する分類メトリックに従って各モジュールのパフォーマンスを確認することです。 meuとしてmodel_evaluation_utilsをインポートします pandasをpdとしてインポートする
basic_cnn_metrics = meu.get_metrics(true_labels = test_labels、predicted_labels = basic_cnn_pred_labels) vgg_frz_metrics = meu.get_metrics(true_labels = test_labels、predicted_labels = vgg_frz_pred_labels) vgg_ft_metrics = meu.get_metrics(true_labels = test_labels、predicted_labels = vgg_ft_pred_labels)
pd.dataframe([basic_cnn_metrics、vgg_frz_metrics、vgg_ft_metrics]、 index = ['Basic cnn'、 'vgg-19 Frozen'、 'vgg-19微調整'])) モデルの精度 3番目のモデルがテストセットで最も優れたパフォーマンスを発揮し、96%の精度のF1スコアを提供します。これは非常に優れており、先に述べた研究論文や記事のより複雑なモデルに匹敵します。 要約するマラリア検査は単純なプロセスではなく、世界中の資格のある人員の不足は、診断と治療の場合に深刻な問題です。マラリアに関する興味深い現実世界の医療画像症例を調べました。 AIを活用できるオープンソースのテクノロジーが簡単に構築されたオープンソーステクノロジーは、マラリアを検出する上で最先端の精度を提供し、AIを社会的に有益にします。 この記事に記載されている記事や研究論文をチェックすることをお勧めします。それなしでは、概念を形成して書き出すことはできません。これらのテクノロジーの実行と採用に興味がある場合、この記事のすべてのコードは私のGitHubリポジトリで入手できます。公式ウェブサイトからデータをダウンロードすることを忘れないでください。 ヘルスケアにより多くのオープンソースAI機能を採用し、世界中でより安価で使いやすくすることを期待しましょう。 |