# Using CNN to Predict Bone age
###### tags: `python` `tensorflow` `NN` `Keras`
## VGG16
### Import library
```python
# Import data & preprocessing
import tensorflow as tf
import keras
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt # showing and rendering figures
# io related
from skimage.io import imread
import os
from glob import glob
# not needed in Kaggle, but required in Jupyter
%matplotlib inline
```
### Import dataset
```python
# Import data
base_bone_dir = os.path.join('.')
print (base_bone_dir)
age_df = pd.read_csv(os.path.join(base_bone_dir, 'boneage-training-dataset.csv'))
ion for now
boneage_mean = 0
boneage_div = 1.0# 製作需要的欄位
age_df['path'] = age_df['id'].map(lambda x: os.path.join(base_bone_dir,
'resize',
'{}.png'.format(x)))
age_df['exists'] = age_df['path'].map(os.path.exists)
print(age_df['exists'].sum(), 'images found of', age_df.shape[0], 'total')
age_df['gender'] = age_df['male'].map(lambda x: 'male' if x else 'female')
boneage_mean = age_df['boneage'].mean()
boneage_div = 2*age_df['boneage'].std()
# we don't want normalizat
age_df['boneage_zscore'] = age_df['boneage'].map(lambda x: (x-boneage_mean)/boneage_div)
age_df.dropna(inplace = True)
age_df['male']= age_df['male'].map(lambda x: 1 if x else 0)
age_df[['boneage', 'male', 'boneage_zscore']].hist(figsize = (10, 5))
age_df['boneage_category'] = pd.cut(age_df['boneage'], 10)
```

```python
age_df['boneage_category'] = pd.cut(age_df['boneage'], 10)
new_age_df = age_df.groupby(['boneage_category', 'male']).apply(lambda x: x.sample(400, replace = True)
).reset_index(drop = True)
print('New Data Size:', new_age_df.shape[0], 'Old Size:', age_df.shape[0])
new_age_df[['boneage', 'male']].hist(figsize = (10, 5))
```
New Data Size: 8000 Old Size: 12611
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x00000134C390E948>,
<matplotlib.axes._subplots.AxesSubplot object at 0x00000134C3C0BB88>]],
dtype=object)

```python
# 將Training & validation 以3:1比例分開
from sklearn.model_selection import train_test_split
raw_train_df, valid_df = train_test_split(age_df,
test_size = 0.25,
random_state = 2018,
stratify = age_df['boneage_category'])
print('train', raw_train_df.shape[0], 'validation', valid_df.shape[0])
```
train 9458 validation 3153
```python
# Balance the distribution in the training set
train_df = raw_train_df.groupby(['boneage_category', 'male']).apply(lambda x: x.sample(500, replace = True)
).reset_index(drop = True)
print('New Data Size:', train_df.shape[0], 'Old Size:', raw_train_df.shape[0])
train_df[['boneage', 'male']].hist(figsize = (10, 5))
# 圖檔預處理
from keras.preprocessing.image import ImageDataGenerator
from keras.applications.vgg16 import preprocess_input
IMG_SIZE = (384, 384) # slightly smaller than vgg16 normally expects
core_idg = ImageDataGenerator(samplewise_center=False,
samplewise_std_normalization=False,
horizontal_flip = True,
vertical_flip = False,
height_shift_range = 0.15,
width_shift_range = 0.15,
rotation_range = 5,
shear_range = 0.01,
fill_mode = 'nearest',
zoom_range=0.25,
preprocessing_function = preprocess_input)
```
New Data Size: 10000 Old Size: 9458
```python
def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, **dflow_args):
base_dir = os.path.dirname(in_df[path_col].values[0])
print('## Ignore next message from keras, values are replaced anyways')
df_gen = img_data_gen.flow_from_directory(base_dir,
class_mode = 'sparse',
**dflow_args)
df_gen.filenames = in_df[path_col].values
df_gen.filepaths.extend(df_gen.filenames)
df_gen.classes = np.stack(in_df[y_col].values)
df_gen.samples = in_df.shape[0]
df_gen.n = in_df.shape[0]
df_gen._set_index_array()
df_gen.directory = '' # since we have the full path
print('Reinserting dataframe: {} images'.format(in_df.shape[0]))
return df_gen
train_gen = flow_from_dataframe(core_idg, train_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = 32)
valid_gen = flow_from_dataframe(core_idg, valid_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = 256) # we can use much larger batches for evaluation
# used a fixed dataset for evaluating the algorithm
test_X, test_Y = next(flow_from_dataframe(core_idg,
valid_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'rgb',
batch_size = 1024)) # one big batch
```
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 10000 images
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 3153 images
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 3153 images
```python
# 確認載入的圖檔
t_x, t_y = next(train_gen)
fig, m_axs = plt.subplots(2, 4, figsize = (16, 8))
for (c_x, c_y, c_ax) in zip(t_x, t_y, m_axs.flatten()):
c_ax.imshow(c_x[:,:,0], cmap = 'bone', vmin = -127, vmax = 127)
c_ax.set_title('%2.0f months' % (c_y*boneage_div+boneage_mean))
c_ax.axis('off')
```

```python
# VGG16參數設定
from keras.applications.vgg16 import VGG16
from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, Input, Conv2D, multiply, LocallyConnected2D, Lambda
from keras.models import Model
in_lay = Input(t_x.shape[1:])
base_pretrained_model = VGG16(input_shape = t_x.shape[1:], include_top = False, weights = 'imagenet')
base_pretrained_model.trainable = False
pt_depth = base_pretrained_model.get_output_shape_at(0)[-1]
pt_features = base_pretrained_model(in_lay)
from keras.layers import BatchNormalization
bn_features = BatchNormalization()(pt_features)
# here we do an attention mechanism to turn pixels in the GAP on an off
attn_layer = Conv2D(64, kernel_size = (1,1), padding = 'same', activation = 'relu')(bn_features)
attn_layer = Conv2D(16, kernel_size = (1,1), padding = 'same', activation = 'relu')(attn_layer)
attn_layer = LocallyConnected2D(1,
kernel_size = (1,1),
padding = 'valid',
activation = 'sigmoid')(attn_layer)
# fan it out to all of the channels
up_c2_w = np.ones((1, 1, 1, pt_depth))
up_c2 = Conv2D(pt_depth, kernel_size = (1,1), padding = 'same',
activation = 'linear', use_bias = False, weights = [up_c2_w])
up_c2.trainable = False
attn_layer = up_c2(attn_layer)
mask_features = multiply([attn_layer, bn_features])
gap_features = GlobalAveragePooling2D()(mask_features)
gap_mask = GlobalAveragePooling2D()(attn_layer)
# to account for missing values from the attention model
gap = Lambda(lambda x: x[0]/x[1], name = 'RescaleGAP')([gap_features, gap_mask])
gap_dr = Dropout(0.5)(gap)
dr_steps = Dropout(0.25)(Dense(1024, activation = 'elu')(gap_dr))
out_layer = Dense(1, activation = 'linear')(dr_steps) # linear is what 16bit did
bone_age_model = Model(inputs = [in_lay], outputs = [out_layer])
# MAE
from keras.metrics import mean_absolute_error
def mae_months(in_gt, in_pred):
return mean_absolute_error(boneage_div*in_gt, boneage_div*in_pred)
bone_age_model.compile(optimizer = 'adam', loss = 'mse',
metrics = [mae_months])
bone_age_model.summary()
```
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.1/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5
58892288/58889256 [==============================] - 32s 1us/step
Model: "model_1"
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
input_1 (InputLayer) (None, 384, 384, 3) 0
__________________________________________________________________________________________________
vgg16 (Model) (None, 12, 12, 512) 14714688 input_1[0][0]
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 12, 12, 512) 2048 vgg16[1][0]
__________________________________________________________________________________________________
conv2d_1 (Conv2D) (None, 12, 12, 64) 32832 batch_normalization_1[0][0]
__________________________________________________________________________________________________
conv2d_2 (Conv2D) (None, 12, 12, 16) 1040 conv2d_1[0][0]
__________________________________________________________________________________________________
locally_connected2d_1 (LocallyC (None, 12, 12, 1) 2448 conv2d_2[0][0]
__________________________________________________________________________________________________
conv2d_3 (Conv2D) (None, 12, 12, 512) 512 locally_connected2d_1[0][0]
__________________________________________________________________________________________________
multiply_1 (Multiply) (None, 12, 12, 512) 0 conv2d_3[0][0]
batch_normalization_1[0][0]
__________________________________________________________________________________________________
global_average_pooling2d_1 (Glo (None, 512) 0 multiply_1[0][0]
__________________________________________________________________________________________________
global_average_pooling2d_2 (Glo (None, 512) 0 conv2d_3[0][0]
__________________________________________________________________________________________________
RescaleGAP (Lambda) (None, 512) 0 global_average_pooling2d_1[0][0]
global_average_pooling2d_2[0][0]
__________________________________________________________________________________________________
dropout_1 (Dropout) (None, 512) 0 RescaleGAP[0][0]
__________________________________________________________________________________________________
dense_1 (Dense) (None, 1024) 525312 dropout_1[0][0]
__________________________________________________________________________________________________
dropout_2 (Dropout) (None, 1024) 0 dense_1[0][0]
__________________________________________________________________________________________________
dense_2 (Dense) (None, 1) 1025 dropout_2[0][0]
==================================================================================================
Total params: 15,279,905
Trainable params: 563,681
Non-trainable params: 14,716,224
__________________________________________________________________________________________________
```python
# VGG16參數設定(?)
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau
weight_path="{}_weights.best.hdf5".format('bone_age')
checkpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1,
save_best_only=True, mode='min', save_weights_only = True)
reduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=10, verbose=1, mode='auto', epsilon=0.0001, cooldown=5, min_lr=0.0001)
early = EarlyStopping(monitor="val_loss",
mode="min",
patience=5) # probably needs to be more patient, but kaggle time is limited
callbacks_list = [checkpoint, early, reduceLROnPlat]
```
C:\Users\Camille_Seknova\anaconda3\envs\tensorflow1\lib\site-packages\keras\callbacks\callbacks.py:998: UserWarning: `epsilon` argument is deprecated and will be removed, use `min_delta` instead.
warnings.warn('`epsilon` argument is deprecated and '
```python
# 開始training
# train_gen.batch_size = 16
bone_age_model.fit_generator(train_gen,
validation_data = (test_X, test_Y),
epochs = 15,
callbacks = callbacks_list)
```
Epoch 1/15
313/313 [==============================] - 6021s 19s/step - loss: 2887.5958 - mae_months: 37.1822 - val_loss: 601.9203 - val_mae_months: 19.8439
Epoch 00001: val_loss improved from inf to 601.92025, saving model to bone_age_weights.best.hdf5
Epoch 2/15
313/313 [==============================] - 5879s 19s/step - loss: 834.0107 - mae_months: 22.8183 - val_loss: 517.4436 - val_mae_months: 18.1152
Epoch 00002: val_loss improved from 601.92025 to 517.44357, saving model to bone_age_weights.best.hdf5
Epoch 3/15
313/313 [==============================] - 5853s 19s/step - loss: 674.4779 - mae_months: 20.3838 - val_loss: 476.4907 - val_mae_months: 17.3899
Epoch 00003: val_loss improved from 517.44357 to 476.49066, saving model to bone_age_weights.best.hdf5
Epoch 4/15
313/313 [==============================] - 5839s 19s/step - loss: 591.0403 - mae_months: 18.9640 - val_loss: 452.4837 - val_mae_months: 16.9138
Epoch 00004: val_loss improved from 476.49066 to 452.48373, saving model to bone_age_weights.best.hdf5
Epoch 5/15
313/313 [==============================] - 5902s 19s/step - loss: 503.1557 - mae_months: 17.5464 - val_loss: 398.2757 - val_mae_months: 15.7191
Epoch 00005: val_loss improved from 452.48373 to 398.27571, saving model to bone_age_weights.best.hdf5
Epoch 6/15
313/313 [==============================] - 5870s 19s/step - loss: 467.0240 - mae_months: 16.8923 - val_loss: 409.5181 - val_mae_months: 15.8301
Epoch 00006: val_loss did not improve from 398.27571
Epoch 7/15
313/313 [==============================] - 5883s 19s/step - loss: 439.9315 - mae_months: 16.3247 - val_loss: 392.8785 - val_mae_months: 15.4780
Epoch 00007: val_loss improved from 398.27571 to 392.87852, saving model to bone_age_weights.best.hdf5
Epoch 8/15
313/313 [==============================] - 5855s 19s/step - loss: 419.6139 - mae_months: 15.9334 - val_loss: 394.2897 - val_mae_months: 15.6384
Epoch 00008: val_loss did not improve from 392.87852
Epoch 9/15
313/313 [==============================] - 5861s 19s/step - loss: 410.1131 - mae_months: 15.7633 - val_loss: 402.8027 - val_mae_months: 15.7428
Epoch 00009: val_loss did not improve from 392.87852
Epoch 10/15
313/313 [==============================] - 5876s 19s/step - loss: 396.9571 - mae_months: 15.4562 - val_loss: 378.6116 - val_mae_months: 15.3340
Epoch 00010: val_loss improved from 392.87852 to 378.61159, saving model to bone_age_weights.best.hdf5
Epoch 11/15
313/313 [==============================] - 6019s 19s/step - loss: 372.0665 - mae_months: 14.9971 - val_loss: 352.4316 - val_mae_months: 14.8172
Epoch 00011: val_loss improved from 378.61159 to 352.43158, saving model to bone_age_weights.best.hdf5
Epoch 12/15
313/313 [==============================] - 6130s 20s/step - loss: 359.2982 - mae_months: 14.8550 - val_loss: 387.3231 - val_mae_months: 15.6298
Epoch 00012: val_loss did not improve from 352.43158
Epoch 13/15
313/313 [==============================] - 6070s 19s/step - loss: 359.9599 - mae_months: 14.8379 - val_loss: 360.8462 - val_mae_months: 15.0052
Epoch 00013: val_loss did not improve from 352.43158
Epoch 14/15
313/313 [==============================] - 6096s 19s/step - loss: 343.5727 - mae_months: 14.5815 - val_loss: 351.4307 - val_mae_months: 14.8820
Epoch 00014: val_loss improved from 352.43158 to 351.43066, saving model to bone_age_weights.best.hdf5
Epoch 15/15
313/313 [==============================] - 6245s 20s/step - loss: 347.8223 - mae_months: 14.5407 - val_loss: 345.8929 - val_mae_months: 14.8902
Epoch 00015: val_loss improved from 351.43066 to 345.89290, saving model to bone_age_weights.best.hdf5
<keras.callbacks.callbacks.History at 0x134cff46c48>
```python
# load the best version of the model
bone_age_model.load_weights(weight_path)
```
```python
# 開始預測
pred_Y = boneage_div*bone_age_model.predict(test_X, batch_size = 32, verbose = True)+boneage_mean
test_Y_months = boneage_div*test_Y+boneage_mean
```
1024/1024 [==============================] - 578s 564ms/step
```python
# 結果放置於 pred_Y 和實際月份 test_Y_months 進行參數比對
fig, ax1 = plt.subplots(1,1, figsize = (6,6))
ax1.plot(test_Y_months, pred_Y, 'r.', label = 'predictions')
ax1.plot(test_Y_months, test_Y_months, 'b-', label = 'actual')
ax1.legend()
ax1.set_xlabel('Actual Age (Months)')
ax1.set_ylabel('Predicted Age (Months)')
```
Text(0, 0.5, 'Predicted Age (Months)')

```python
from sklearn.metrics import mean_absolute_error, median_absolute_error
print(mean_absolute_error(pred_Y, test_Y_months))
print(median_absolute_error(pred_Y, test_Y_months))
```
14.890207
12.889206
## MobieNet
* 啟動Anaconda powershell 切到工作目錄下,將影像dataset跟 boneage-training-dataset.csv 都放在同一目錄下

* Import data & preprocessing
*
```python
import tensorflow as tf
import keras
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import matplotlib.pyplot as plt # showing and rendering figures
# io related
from skimage.io import imread
import os
from glob import glob
# not needed in Kaggle, but required in Jupyter
%matplotlib inline
```
* Import data
```python
#base_bone_dir = os.path.join('c:', os.sep, 'Users', 'Elfay', 'ML')
base_bone_dir = os.path.join('.')
print (base_bone_dir)
age_df = pd.read_csv(os.path.join(base_bone_dir,'boneage-training-dataset.csv'))
age_df.describe
```
.
<index id boneage male
0 1377 180 0
1 1378 12 0
2 1379 94 0
3 1380 120 1
4 1381 82 0
... ... ... ...
[12611 rows x 3 columns]>
* 製作需要的欄位
```python
age_df['path'] = age_df['id'].map(lambda x: os.path.join(base_bone_dir,
'resize',
'{}.png'.format(x)))
age_df['exists'] = age_df['path'].map(os.path.exists)
print(age_df['exists'].sum(), 'images found of', age_df.shape[0], 'total')
age_df['gender'] = age_df['male'].map(lambda x: 'male' if x else 'female')
boneage_mean = age_df['boneage'].mean()
boneage_div = 2*age_df['boneage'].std()
age_df['boneage_zscore'] = age_df['boneage'].map(lambda x: (x-boneage_mean)/boneage_div)
age_df.dropna(inplace = True)
age_df.sample(3)
```
12611 images found of 12611 total
* 完成之CSV檔案預覽
<div>
<style scoped>
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
</style>
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>id</th>
<th>boneage</th>
<th>male</th>
<th>path</th>
<th>exists</th>
<th>gender</th>
<th>boneage_zscore</th>
</tr>
</thead>
<tbody>
<tr>
<th>2770</th>
<td>4650</td>
<td>132</td>
<td>0</td>
<td>.\resize\4650.png</td>
<td>True</td>
<td>female</td>
<td>0.056812</td>
</tr>
<tr>
<th>10121</th>
<td>12828</td>
<td>120</td>
<td>0</td>
<td>.\resize\12828.png</td>
<td>True</td>
<td>female</td>
<td>-0.088883</td>
</tr>
<tr>
<th>4176</th>
<td>6195</td>
<td>162</td>
<td>0</td>
<td>.\resize\6195.png</td>
<td>True</td>
<td>female</td>
<td>0.421048</td>
</tr>
</tbody>
</table>
</div>
* 展示資料分佈狀況
```python
age_df[['boneage','male', 'boneage_zscore']].hist(figsize = (10, 5))
```
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000000000FE5DE88>,
<matplotlib.axes._subplots.AxesSubplot object at 0x00000000100AEA88>],
[<matplotlib.axes._subplots.AxesSubplot object at 0x00000000100E9448>,
<matplotlib.axes._subplots.AxesSubplot object at 0x000000001011FE08>]],
dtype=object)

* 按照boneage & 性別分布取8000筆資料
```python
age_df['boneage_category'] = pd.cut(age_df['boneage'], 10)
new_age_df = age_df.groupby(['boneage_category', 'male']).apply(lambda x: x.sample(400, replace = True)
).reset_index(drop = True)
print('New Data Size:', new_age_df.shape[0], 'Old Size:', age_df.shape[0])
new_age_df[['boneage', 'male']].hist(figsize = (10, 5))
```
New Data Size: 8000 Old Size: 12611

* 將Training & validation 以3:1比例分開
```python
from sklearn.model_selection import train_test_split
train_df, valid_df = train_test_split(new_age_df,
test_size = 0.25,
random_state = 2018,
stratify = new_age_df['boneage_category'])
print('train', train_df.shape[0], 'validation', valid_df.shape[0])
```
train 6000 validation 2000
* 圖檔預處理
```python
from keras.preprocessing.image import ImageDataGenerator
IMG_SIZE = (256, 256)
core_idg = ImageDataGenerator(samplewise_center=True,
samplewise_std_normalization=True,
horizontal_flip = True,
vertical_flip = False,
height_shift_range = 0.1,
width_shift_range = 0.1,
rotation_range = 10,
shear_range = 0.05,
fill_mode = 'nearest',
zoom_range=0.15)
```
* 載入圖檔
```python
def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, **dflow_args):
base_dir = os.path.dirname(in_df[path_col].values[0])
print('## Ignore next message from keras, values are replaced anyways')
df_gen = img_data_gen.flow_from_directory(base_dir,
class_mode = 'sparse',
**dflow_args)
df_gen.filenames = in_df[path_col].values
df_gen.filepaths.extend(df_gen.filenames)
df_gen.classes = np.stack(in_df[y_col].values)
df_gen.samples = in_df.shape[0]
df_gen.n = in_df.shape[0]
df_gen._set_index_array()
df_gen.directory = '' # since we have the full path
print('Reinserting dataframe: {} images'.format(in_df.shape[0]))
return df_gen
```
```python
train_gen = flow_from_dataframe(core_idg, train_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'grayscale',
batch_size = 64)
valid_gen = flow_from_dataframe(core_idg, valid_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'grayscale',
batch_size = 64) # we can use much larger batches for evaluation
# used a fixed dataset for evaluating the algorithm
test_X, test_Y = next(flow_from_dataframe(core_idg,
valid_df,
path_col = 'path',
y_col = 'boneage_zscore',
target_size = IMG_SIZE,
color_mode = 'grayscale',
batch_size = 500)) # one big batch
```
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 6000 images
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 2000 images
## Ignore next message from keras, values are replaced anyways
Found 0 images belonging to 0 classes.
Reinserting dataframe: 2000 images
* 確認載入的圖檔
```python
t_x, t_y = next(train_gen)
fig, m_axs = plt.subplots(4, 4, figsize = (16, 16))
for (c_x, c_y, c_ax) in zip(t_x, t_y, m_axs.flatten()):
c_ax.imshow(c_x[:,:,0], cmap = 'bone', vmin = -3, vmax = 3)
c_ax.set_title('%2.0f months' % (c_y*boneage_div+boneage_mean))
c_ax.axis('off')
```

* MobileNet 參數設定
```python
from keras.applications.mobilenet import MobileNet
from keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, BatchNormalization
from keras.models import Sequential
base_mobilenet_model = MobileNet(input_shape = t_x.shape[1:],
include_top = False,
weights = None)
bone_age_model = Sequential()
bone_age_model.add(BatchNormalization(input_shape = t_x.shape[1:]))
bone_age_model.add(base_mobilenet_model)
bone_age_model.add(BatchNormalization())
bone_age_model.add(GlobalAveragePooling2D())
bone_age_model.add(Dropout(0.5))
bone_age_model.add(Dense(1, activation = 'linear' )) # linear is what 16bit did
from keras.metrics import mean_absolute_error
def mae_months(in_gt, in_pred):
return mean_absolute_error(boneage_div*in_gt, boneage_div*in_pred)
bone_age_model.compile(optimizer = 'adam', loss = 'mse',
metrics = [mae_months])
bone_age_model.summary()
```
Model: "sequential_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
batch_normalization_1 (Batch (None, 256, 256, 1) 4
_________________________________________________________________
mobilenet_1.00_256 (Model) (None, 8, 8, 1024) 3228288
_________________________________________________________________
batch_normalization_2 (Batch (None, 8, 8, 1024) 4096
_________________________________________________________________
global_average_pooling2d_1 ( (None, 1024) 0
_________________________________________________________________
dropout_1 (Dropout) (None, 1024) 0
_________________________________________________________________
dense_1 (Dense) (None, 1) 1025
=================================================================
Total params: 3,233,413
Trainable params: 3,209,475
Non-trainable params: 23,938
_________________________________________________________________
* MobileNet 參數設定
```python
from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau
weight_path="{}_weights.best.hdf5".format('bone_age')
checkpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1,
save_best_only=True, mode='min', save_weights_only = True)
reduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=10, verbose=1, mode='auto', epsilon=0.0001, cooldown=5, min_lr=0.0001)
early = EarlyStopping(monitor="val_loss",
mode="min",
patience=5) # probably needs to be more patient, but kaggle time is limited
callbacks_list = [checkpoint, early, reduceLROnPlat]
```
* 開始training,訓練參數置入 bone_age_weights.best.hdf5
```python
train_gen.batch_size = 16
bone_age_model.fit_generator(train_gen,
validation_data = (test_X, test_Y),
epochs = 10,
callbacks = callbacks_list)
```
Epoch 1/10
375/375 [==============================] - 1261s 3s/step - loss: 0.6289 - mae_months: 52.4197 - val_loss: 0.6183 - val_mae_months: 56.1310
Epoch 00001: val_loss improved from inf to 0.61833, saving model to bone_age_weights.best.hdf5
Epoch 2/10
375/375 [==============================] - 1219s 3s/step - loss: 0.3611 - mae_months: 39.8186 - val_loss: 0.4336 - val_mae_months: 46.1007
Epoch 00002: val_loss improved from 0.61833 to 0.43358, saving model to bone_age_weights.best.hdf5
Epoch 3/10
375/375 [==============================] - 1275s 3s/step - loss: 0.2738 - mae_months: 34.3837 - val_loss: 0.2607 - val_mae_months: 32.8493
Epoch 00003: val_loss improved from 0.43358 to 0.26069, saving model to bone_age_weights.best.hdf5
Epoch 4/10
375/375 [==============================] - 1275s 3s/step - loss: 0.2304 - mae_months: 31.2175 - val_loss: 0.4354 - val_mae_months: 45.7760
Epoch 00004: val_loss did not improve from 0.26069
Epoch 5/10
375/375 [==============================] - 1276s 3s/step - loss: 0.1979 - mae_months: 28.9312 - val_loss: 0.1548 - val_mae_months: 24.7830
Epoch 00005: val_loss improved from 0.26069 to 0.15478, saving model to bone_age_weights.best.hdf5
Epoch 6/10
375/375 [==============================] - 1279s 3s/step - loss: 0.1733 - mae_months: 27.0213 - val_loss: 0.1598 - val_mae_months: 25.1762
Epoch 00006: val_loss did not improve from 0.15478
Epoch 7/10
375/375 [==============================] - 1289s 3s/step - loss: 0.1580 - mae_months: 25.6752 - val_loss: 0.1208 - val_mae_months: 22.1466
Epoch 00007: val_loss improved from 0.15478 to 0.12078, saving model to bone_age_weights.best.hdf5
Epoch 8/10
375/375 [==============================] - 1290s 3s/step - loss: 0.1391 - mae_months: 24.2044 - val_loss: 0.1435 - val_mae_months: 24.5172
Epoch 00008: val_loss did not improve from 0.12078
Epoch 9/10
375/375 [==============================] - 1303s 3s/step - loss: 0.1306 - mae_months: 23.4019 - val_loss: 0.0976 - val_mae_months: 19.3900
Epoch 00009: val_loss improved from 0.12078 to 0.09761, saving model to bone_age_weights.best.hdf5
Epoch 10/10
375/375 [==============================] - 1278s 3s/step - loss: 0.1241 - mae_months: 22.9744 - val_loss: 0.0910 - val_mae_months: 19.1049
Epoch 00010: val_loss improved from 0.09761 to 0.09099, saving model to bone_age_weights.best.hdf5
### 預測資料
* 載入訓練參數 bone_age_weights.best.hdf5
```python
weight_path=
bone_age_model.load_weights(weight_path)
```
* 開始預測
```python
pred_Y = boneage_div*bone_age_model.predict(test_X, batch_size = 16, verbose = True)+boneage_mean
test_Y_months = boneage_div*test_Y+boneage_mean
```
500/500 [==============================] - 33s 67ms/step
* 結果放置於 pred_Y 和實際月份 test_Y_months 進行參數比對
```python
fig, ax1 = plt.subplots(1,1, figsize = (6,6))
ax1.plot(test_Y_months, pred_Y, 'r.', label = 'predictions')
ax1.plot(test_Y_months, test_Y_months, 'b-', label = 'actual')
ax1.legend()
ax1.set_xlabel('Actual Age (Months)')
ax1.set_ylabel('Predicted Age (Months)')
```
* 結果展示

```python
from sklearn.metrics import mean_absolute_error, median_absolute_error
print(mean_absolute_error(pred_Y, test_Y_months))
print(median_absolute_error(pred_Y, test_Y_months))
```
### 改影像前處理
## Reference
* [VGG16](https://www.kaggle.com/kmader/attention-on-pretrained-vgg16-for-bone-age)
* [InceptionV3](https://www.kaggle.com/kmader/pretrained-inceptionv3-for-bone-age)
* [MobileNet](https://www.kaggle.com/kmader/mobilenet-for-bone-age)
* [MobileNet Introduction](https://medium.com/@chih.sheng.huang821/%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92-mobilenet-depthwise-separable-convolution-f1ed016b3467)
* [RestNet50](https://www.kaggle.com/suniliitb96/tutorial-keras-transfer-learning-with-resnet50)