脳腫瘍判別システム

# はじめに
はじめまして。アイデミーでAIを学んでいるTakutoです。
システムエンジニアとして働いており、前職にて製品の欠陥検出を行うシステム開発をしておりました。

欠陥検出システムについて説明しますと、製品をカメラで撮影し、その取得した画像から製品が不良かどうかを判別するシステムです。

今回AIを学ぼうと思った理由ですが、前職でAIに関わるプロジェクトを経験したからです。
AIの判定部分は別部署が担当していたのですが、システムを開発する中で、その部署とのやり取りがうまくかみ合わないことが多々ありました。

システムを開発する上で、自分もAIを理解した方が開発がより進むと思いましたので、AIを学ぼうと決意しました。

最後の課題として、AIアプリを開発しましたので紹介したいと思います。

# 作成した脳腫瘍アプリについて

### 脳腫瘍アプリの概要

今回作成したアプリですが、「脳腫瘍の判別システム」になります。

[脳腫瘍判別アプリ](https://flask-brain-app.onrender.com/)

下記手順に従って動作致します。

1. MRIで撮影した脳の画像を用意する
1. 「ファイル選択」を押して、用意した脳の画像を選択
1. 「submit!」を押して、判定

今回使用した画像はKaggleのコンペに用意されております。
試してみたい方は、下記サイトからデータをダウンロードしてみてください。

[画像データはここからダウンロード](https://www.kaggle.com/datasets/ahmedhamada0/brain-tumor-detection)

### 脳腫瘍アプリの開発環境

アプリを構築のための環境について、ライブラリを含めて表にまとめました。

|プログラミング言語及び、ライブラリ名  |version  |
|:-:|:-:|
|Python  |3.10.11  |
|flask | 2.0.1 |
|Gunicorn|20.0.4|
|tensorflow|2.11.0|
|Werkzeug|2.0.0|
|pillow|7.2.0|

### 脳腫瘍判別アプリを作成した経緯

元々製品の欠陥検出していた人間が、なぜ脳腫瘍判別アプリ開発に挑戦したかと言いますと

* 画像を使ったシステムを開発したい
* 日本の深刻な医者不足

の2つが理由になります。

日本は先進国の中でも医師数が少なく、長時間労働にならざるを得ない状況です。

[参考サイト](https://hodanren.doc-net.or.jp/news/iryounews/200605_sisk3_cvd_doc.html)

これからどんどん少子高齢化が進んでいくと、この問題はより一層深くなっていきます。

そう思ったら、医者の数を増やす努力だけでなく、最新の技術を用いて、医師の力になることも大事だと感じたわけです。
だからこそ、脳腫瘍の判別アプリを開発してみました。

# 作成したプログラム

アプリの根幹となっているAI部分のプログラムの解説を行いたいと思います。
作成したコードは以下の通りです。

```make_model.py
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import Dense, Dropout, Flatten, Input, GlobalAveragePooling2D, Activation
from tensorflow.keras.layers import Conv2D, MaxPooling2D
from tensorflow.keras.applications.vgg16 import VGG16
from tensorflow.keras.models import Model, Sequential
from tensorflow.keras import optimizers
from tensorflow.keras.optimizers import Adam

#モデルの保存
from google.colab import files


# Google Drive上のフォルダを選択
path_brain_yes = os.listdir('/content/drive/MyDrive/kaggle/archive/yes')
path_brain_no = os.listdir('/content/drive/MyDrive/kaggle/archive/no')

#set input size
INPUT_SIZE = 64

img_brain_yes = []
img_brain_no = []

for i in range(len(path_brain_yes)):
    img = cv2.imread('/content/drive/MyDrive/kaggle/archive/yes/' + path_brain_yes[i])
    img = cv2.resize(img, (INPUT_SIZE,INPUT_SIZE))
    img_brain_yes.append(img)

for i in range(len(path_brain_no)):
    img = cv2.imread('/content/drive/MyDrive/kaggle/archive/no/' + path_brain_no[i])
    img = cv2.resize(img, (INPUT_SIZE,INPUT_SIZE))
    img_brain_no.append(img)

X = np.array(img_brain_yes + img_brain_no)
y =  np.array([0]*len(img_brain_yes) + [1]*len(img_brain_no))

rand_index = np.random.permutation(np.arange(len(X)))
X = X[rand_index]
y = y[rand_index]

split_ratio = 0.8

# データの分割
X_train = X[:int(len(X)*split_ratio)]
y_train = y[:int(len(y)*split_ratio)]
X_test = X[int(len(X)*split_ratio):]
y_test = y[int(len(y)*split_ratio):]

# vgg16のインスタンスの生成
#---------------------------
input_tensor = Input(shape=(INPUT_SIZE, INPUT_SIZE, 3))
vgg16 = VGG16(include_top=False, weights='imagenet', input_tensor=input_tensor)
#---------------------------

# 重みの固定
#---------------------------
for layer in vgg16.layers:
    layer.trainable = False
#---------------------------

# 全結合層の構築
x = vgg16.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
x = Dropout(0.5)(x)
predictions = Dense(1, activation="sigmoid")(x)

# モデルの連結
#---------------------------
model = Model(inputs=vgg16.input, outputs=predictions)
#---------------------------

optimizer = Adam()

model.compile(loss='binary_crossentropy',
              optimizer=optimizer,
              metrics=['accuracy'])

batch_size = 32
epochs = 10

#学習過程の取得
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs,validation_data=(X_test, y_test))

# 精度の評価
scores = model.evaluate(X_test, y_test, verbose=1)
print('Test loss:', scores[0])
print('Test accuracy:', scores[1])

```

このコードの中での工夫としては、この2つだ。
それぞれについて解説する。

```工夫1 .py
x = GlobalAveragePooling2D()(x)
```

```工夫2 .py
optimizer = Adam()
```

### グローバル平均プーリング(Global Average Pooling)

グローバル平均プーリングを用いるメリットとしては、

* 訓練に必要なパラメータを大幅に削減することができる(メモリ使用量は少なくなる)
* パラメータ削減することで過学習のリスクを低減することができる

の2点があげられます。

下記サイトだとイメージしやすいですね。

[グローバル平均プーリング参考記事](https://qiita.com/mine820/items/1e49bca6d215ce88594a)


### Adam

Adamとは最適化手法のことです。

言い換えると、「ディープラーニングを行う際に伴う損失を限りになくゼロにする」ことになります。

損失をゼロにすることで、効率よく学習を行うことができ、性能の高いモデルを作成することが可能になります。
下記サイトが分かりやすいですね。

[Adam(最適化手法)参考記事](https://www.tech-teacher.jp/blog/algorithm-machine-learning/)

### 精度と損失率

実際に上記のプログラムで学習した際の精度と損失率の結果は以下の通りです。

Test loss(損失率): 0.14728868007659912
Test accuracy(精度): 0.9560669660568237

損失率は低く、精度もかなり高くすることができました。
これはAdamを利用したことがかなり大きいかと思います。

### 学習時の精度と損失率の推移のグラフ

学習した際に過学習が起きてないか確認してみました。
精度(accuracy)と損失率(loss)のグラフは下記の通りです。

<p>

<img width="310" img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455120/4862bb46-3a67-0207-4294-ddb9ae66e47f.png" />

<img width="300" img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3455120/99010dec-0ae4-8e46-02b2-b4fa6a67d664.png" />

</p>

Epoch数が増えるにしたがって、正答率は上昇し、損失率は低下していることがグラフから分かります。
テストデータも増加と減少は繰り返しているものの、同様な動きをしている為、過学習はしていないと考えております。

# 今後の活用
アイデミーで学んだことを活かして、AIを用いたアプリ開発を行いました。
実は既に転職し、欠陥検出からは離れてしまったのですが、システム開発はこれからも行っていきます。
その中で、AIを用いることが出てきたら、学んだことを活かしていこうと思います。

転職先でもAIはコア技術の為、人材を育成していくみたいですので、その中で自分の力を発揮できると考えております。

# おわりに

講座を通して、AIについて学びましたが、パラメータの調整やネットワークの構築が非常に手間だと感じました。前職でAIのプロジェクトに参画した際には、AI開発は別部門が担当していたため、時間をかけて調整してくださったんだなと思うと、学んで良かったなと感じます。

自分は前職でAIプロジェクトの一員であったことから、AIのメリットだけでなく、デメリットについても痛いほど理解しております。
その経験にプラスして、AI開発の一端に触れたので、AIに関して提言できる立場にはなれたのではないかと

だから、今の会社でAI開発に参画することになった際には、AIとは何かってのを忖度なく伝えていこうと思います。

また、脳腫瘍のことでも書かせていただきましたが、AIによって救われるものが多くあると思いますので、AIでよりより未来を作っていきたいと思います。