ホーム » 「TensorFlow 2.0 Tutorials (Alpha)」タグがついた投稿

タグアーカイブ: TensorFlow 2.0 Tutorials (Alpha)

TensorFlow 2.0 Alpha : Tutorials : Estimator :- Estimator で線形モデルを構築する

TensorFlow 2.0 Alpha : Beginner Tutorials : Estimator :- Estimator で線形モデルを構築する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/05/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Estimators の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Estimator :- Estimator で線形モデルを構築する

概要

この end-to-end ウォークスルーは tf.estimator API を使用してロジスティック回帰モデルを訓練します。このモデルはしばしば他の、より複雑な、アルゴリズムのためのベースラインとして使用されます。

 

セットアップ

!pip install -q sklearn
from __future__ import absolute_import, division, print_function

import os
import sys

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import clear_output
from six.moves import urllib

 

タイタニック・データセットをロードする

タイタニック・データセットを、性別、年齢、クラス, etc. のような特質が与えられたときに乗客の生存を予測するという (寧ろ憂鬱な) 目標で使用します。

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow.compat.v2.feature_column as fc

import tensorflow as tf
# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')

 

データを調べる

データセットは次の特徴を含みます。

dftrain.head()

sex age n_siblings_spouses parch fare class deck embark_town alone
0 male 22.0 1 0 7.2500 Third unknown Southampton n
1 female 38.0 1 0 71.2833 First C Cherbourg n
2 female 26.0 0 0 7.9250 Third unknown Southampton y
3 female 35.0 1 0 53.1000 First C Southampton n
4 male 28.0 0 0 8.4583 Third unknown Queenstown y

dftrain.describe()

age n_siblings_spouses parch fare
count 627.000000 627.000000 627.000000 627.000000
mean 29.631308 0.545455 0.379585 34.385399
std 12.511818 1.151090 0.792999 54.597730
min 0.750000 0.000000 0.000000 0.000000
25% 23.000000 0.000000 0.000000 7.895800
50% 28.000000 0.000000 0.000000 15.045800
75% 35.000000 1.000000 0.000000 31.387500
max 80.000000 8.000000 5.000000 512.329200

訓練と評価セットにはそれぞれ 627 と 264 サンプルがあります。

dftrain.shape[0], dfeval.shape[0]
(627, 264)

乗客の大多数は 20 代と 30 代です。

dftrain.age.hist(bins=20);

乗船した男性の乗客は女性の乗客のおよそ 2 倍いました。

dftrain.sex.value_counts().plot(kind='barh');

乗客の多数は “third” クラスでした。

dftrain['class'].value_counts().plot(kind='barh');

女性は男性に比較して遥かに高い生存のチャンスを持ちます。これは明らかにモデルのための予測的な特徴となるでしょう。

pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive');

 

モデルのための特徴量エンジニアリング

Estimator はモデルが生入力特徴の各々をどのように解釈するべきかを記述する feature columns と呼ばれるシステムを使用します。Estimator は数値入力のベクトルを想定し、feature columns はモデルが特徴の各々をどのように変換するべきかを記述します。

feature columns の正しいセットを選択して巧妙に作成することは効果的なモデルを学習するためのキーです。feature column は元の特徴辞書の生入力の一つであるか (ベース feature column)、一つまたは複数のベースカラムに渡り定義された transformation を使用して作成された任意の新しいカラム (派生 feature column) であり得ます。

線形 estimator は numereic と categorical feature の両者を使用します。feature columns は総ての TensorFlow estimator と動作してそしてそれらの目的はモデリングのために使用される特徴を定義することです。更に、それらは one-hot-エンコーディング, 正規化と bucketization のような幾つかの特徴量エンジニアリング機能を提供します。

 

ベース Feature Columns

CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']

feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(tf.feature_column.categorical_column_with_vocabulary_list(feature_name, vocabulary))
  
for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(tf.feature_column.numeric_column(feature_name, dtype=tf.float32))

input_function はデータがどのように (ストリーミング流儀で入力パイプラインに供給する) tf.data.Dataset に変換されるかを指定します。tf.data.Dataset は dataframe, csv-形式ファイル等のような複数のソースを取ります。

def make_input_fn(data_df, label_df, num_epochs=10, shuffle=True, batch_size=32):
  def input_function():
    ds = tf.data.Dataset.from_tensor_slices((dict(data_df), label_df))
    if shuffle:
      ds = ds.shuffle(1000)
    ds = ds.batch(batch_size).repeat(num_epochs)
    return ds
  return input_function

train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, num_epochs=1, shuffle=False)

データセットを調べることができます :

ds = make_input_fn(dftrain, y_train, batch_size=10)()
for feature_batch, label_batch in ds.take(1):
  print('Some feature keys:', list(feature_batch.keys()))
  print()
  print('A batch of class:', feature_batch['class'].numpy())
  print()
  print('A batch of Labels:', label_batch.numpy())
Some feature keys: ['embark_town', 'class', 'parch', 'n_siblings_spouses', 'deck', 'sex', 'age', 'fare', 'alone']

A batch of class: [b'Third' b'Third' b'Third' b'Third' b'Third' b'Third' b'First' b'Third'
 b'Third' b'Third']

A batch of Labels: [0 1 0 0 0 0 0 1 0 0]

tf.keras.layers.DenseFeatures 層を使用して特定の feature column の結果を調べることもできます :

age_column = feature_columns[7]
tf.keras.layers.DenseFeatures([age_column])(feature_batch).numpy()
WARNING: Logging before flag parsing goes to stderr.
W0307 18:19:20.561428 140073716143872 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/feature_column/feature_column_v2.py:2758: to_float (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.

array([[28. ],
       [32. ],
       [23. ],
       [28. ],
       [29. ],
       [55.5],
       [40. ],
       [ 5. ],
       [34. ],
       [34. ]], dtype=float32)

DenseFeatures は密 tensor だけを受け取ります、categorical column を調べるためにはそれを indicator column に最初に変形する必要があります :

gender_column = feature_columns[0]
tf.keras.layers.DenseFeatures([tf.feature_column.indicator_column(gender_column)])(feature_batch).numpy()
W0307 18:19:20.583961 140073716143872 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/ops/lookup_ops.py:1347: to_int64 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use `tf.cast` instead.
W0307 18:19:20.585764 140073716143872 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/feature_column/feature_column_v2.py:4307: IndicatorColumn._variable_shape (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.
W0307 18:19:20.586556 140073716143872 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/feature_column/feature_column_v2.py:4362: VocabularyListCategoricalColumn._num_buckets (from tensorflow.python.feature_column.feature_column_v2) is deprecated and will be removed in a future version.
Instructions for updating:
The old _FeatureColumn APIs are being deprecated. Please use the new FeatureColumn APIs instead.

array([[1., 0.],
       [1., 0.],
       [0., 1.],
       [1., 0.],
       [1., 0.],
       [1., 0.],
       [1., 0.],
       [0., 1.],
       [1., 0.],
       [1., 0.]], dtype=float32)

総てのベース feature をモデルに追加した後、モデルを訓練しましょう。モデルの訓練は tf.estimator API を使用して単一のコマンドだけです :

linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)
{'label/mean': 0.375, 'loss': 0.52442783, 'auc': 0.82681364, 'global_step': 200, 'recall': 0.7878788, 'auc_precision_recall': 0.7858568, 'accuracy': 0.7537879, 'average_loss': 0.5273171, 'prediction/mean': 0.4911321, 'precision': 0.6393443, 'accuracy_baseline': 0.625}

 

派生 Featuer Columns

今では 75% の精度に達しました。各ベース feature column の単独の使用はデータを説明するために十分ではないかもしれません。例えば、性別とラベルの間の相関性は異なる性別に対して異なるかもしれません。それゆえに、gender=”Male” と gender=”Female” のために単一のモデル重みを学習だけれあれば、総ての age-gender 組み合わせを補足しないでしょう (e.g. gender=”Male” AND age=”30″ AND gender=”Male” AND age=”40″ 間を識別する)。

異なる特徴の組み合わせ間の違いを学習するために、crossed feature columns をモデルに追加することができます (cross column の前に age column を分類する (= bucketize) こともできます) :

age_x_gender = tf.feature_column.crossed_column(['age', 'sex'], hash_bucket_size=100)

モデルに combination feature を追加した後、モデルを再度訓練しましょう :

derived_feature_columns = [age_x_gender]
linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns+derived_feature_columns)
linear_est.train(train_input_fn)
result = linear_est.evaluate(eval_input_fn)

clear_output()
print(result)
{'label/mean': 0.375, 'loss': 0.46076283, 'auc': 0.84664834, 'global_step': 200, 'recall': 0.64646465, 'auc_precision_recall': 0.79672474, 'accuracy': 0.7537879, 'average_loss': 0.4691896, 'prediction/mean': 0.40174615, 'precision': 0.68085104, 'accuracy_baseline': 0.625}

それは今では 77.6% の精度を獲得しました、これはベース feature でのみ訓練されたものよりも僅かに良いです。更に上手くやれるかを見るためにより多くの features と transformations を使用してみることができます!

今では評価セットからの乗客上で予測を行なうために訓練モデルを使用できます。TensorFlow モデルはサンプルのバッチ、コレクション上で一度に予測を行なうために最適化されています。先に、評価セット全体を使用して eval_input_fn が定義されました。

pred_dicts = list(linear_est.predict(eval_input_fn))
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])

probs.plot(kind='hist', bins=20, title='predicted probabilities')
<matplotlib.axes._subplots.AxesSubplot at 0x7f64c44c5d30>

最期に、結果の ROC (receiver operating characteristic) を見ます、これは真陽性と偽陽性率の間のトレードオフのより良い考えを与えてくれます。

from sklearn.metrics import roc_curve
from matplotlib import pyplot as plt

fpr, tpr, _ = roc_curve(y_eval, probs)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.xlim(0,)
plt.ylim(0,)
(0, 1.05)

 

以上



TensorFlow 2.0 Alpha : Tutorials : Estimator :- 勾配ブースティング木: モデル理解

TensorFlow 2.0 Alpha : Beginner Tutorials : Estimator :- 勾配ブースティング木: モデル理解 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/04/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Estimators の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

 

Estimator :- 勾配ブースティング木: モデル理解

勾配ブースティング・モデルを訓練する end-to-end ウォークスルーについては ブースティング木チュートリアル を調べてください。このチュートリアルでは貴方は :

  • ブースティング木モデルをローカルとグルーバルの両者でどのように解釈するかを学習します。
  • ブースティング木モデルがデータセットにどのように fit するかについて直感を得ます。

 

ブースティング木モデルをローカルとグローバルの両者でどのように解釈するか

ローカル解釈可能性 (= interpretability) は個別のサンプルレベルでのモデルの予測の理解を参照し、その一方で、グルーバル解釈可能性は全体としてのモデルの理解を参照します。そのようなテクニックは機械学習 (ML) 実践者にモデル開発段階でバイアスとバグを検出する助けとなれます。

ローカル解釈可能性については、どのようにインスタンス毎の寄与を作成して可視化するかを学習します。これを特徴量重要度と区別するため、これらの値を DFC (directional feature contributions) として参照します。

グローバル解釈可能性については、gain-based 特徴量重要度、再配列 (= permutation) 特徴量重要度 を取得して可視化し、そしてまた総計 (= aggregated) DFC を示します。

 

タイタニック・データセットをロードします

タイタニック・データセットを使用します、そこでは (寧ろ憂鬱な) 目標は性別、年齢、クラス, etc. のような特質が与えられたときに乗客の生存を予測することです。

from __future__ import absolute_import, division, print_function

import numpy as np
import pandas as pd
from IPython.display import clear_output

# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')

!pip install -q tensorflow==2.0.0-alpha0

import tensorflow as tf
tf.random.set_seed(123)

特徴の説明については、前のチュートリアルを見直してください。

 

feature columns, input_fn を作成して estimator を訓練する

データを前処理する

オリジナルの numeric column そのままと one-hot-エンコーディング categorical 変数を使用して feature column を作成します。

fc = tf.feature_column
CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']
  
def one_hot_cat_column(feature_name, vocab):
  return fc.indicator_column(
      fc.categorical_column_with_vocabulary_list(feature_name,
                                                 vocab))
feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  # Need to one-hot encode categorical features.
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))
  
for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(fc.numeric_column(feature_name,
                                           dtype=tf.float32))

 

入力パイプラインを構築する

Pandas から直接データを読み込むために tf.data API の from_tensor_slices メソッドを使用して入力関数を作成します。

# Use entire batch since this is such a small dataset.
NUM_EXAMPLES = len(y_train)

def make_input_fn(X, y, n_epochs=None, shuffle=True):
  def input_fn():
    dataset = tf.data.Dataset.from_tensor_slices((X.to_dict(orient='list'), y))
    if shuffle:
      dataset = dataset.shuffle(NUM_EXAMPLES)
    # For training, cycle thru dataset as many times as need (n_epochs=None).    
    dataset = (dataset
      .repeat(n_epochs)
      .batch(NUM_EXAMPLES)) 
    return dataset
  return input_fn

# Training and evaluation input functions.
train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)

 

モデルを訓練する

params = {
  'n_trees': 50,
  'max_depth': 3,
  'n_batches_per_layer': 1,
  # You must enable center_bias = True to get DFCs. This will force the model to 
  # make an initial prediction before using any features (e.g. use the mean of 
  # the training labels for regression or log odds for classification when
  # using cross entropy loss).
  'center_bias': True
}

est = tf.estimator.BoostedTreesClassifier(feature_columns, **params)
# Train model.
est.train(train_input_fn, max_steps=100)

# Evaluation.
results = est.evaluate(eval_input_fn)
clear_output()
pd.Series(results).to_frame()

accuracy 0.806818
accuracy_baseline 0.625000
auc 0.866606
auc_precision_recall 0.849128
average_loss 0.421549
global_step 100.000000
label/mean 0.375000
loss 0.421549
precision 0.755319
prediction/mean 0.384944
recall 0.717172

 

モデル解釈とプロット

import matplotlib.pyplot as plt
import seaborn as sns
sns_colors = sns.color_palette('colorblind')

 

ローカル解釈可能性

次に個々の予測を説明するために Palczewska et alInterpreting Random Forests で Saabas により概説されたアプローチを使用して DFC (directional feature contributions) を出力します (このメソッドはまた treeinterpreter パッケージで Random Forests のための scikit-learn でも利用可能です)。

DFC は次で生成されます :

pred_dicts = list(est.experimental_predict_with_explanations(pred_input_fn))

(Note: このメソッドは experimental として命名されていますがこれは experimental prefix を破棄する前に API を変更するかもしれないためです。)

pred_dicts = list(est.experimental_predict_with_explanations(eval_input_fn))
# Create DFC Pandas dataframe.
labels = y_eval.values
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])
df_dfc = pd.DataFrame([pred['dfc'] for pred in pred_dicts])
df_dfc.describe().T

count mean std min 25% 50% 75% max
age 264.0 -0.027537 0.080741 -0.139372 -0.076765 -0.052056 0.002137 0.372113
sex 264.0 0.006734 0.106714 -0.119415 -0.072686 -0.071646 0.136922 0.179607
class 264.0 0.016440 0.091105 -0.054638 -0.045972 -0.045116 0.034190 0.227794
deck 264.0 -0.016943 0.029100 -0.060259 -0.041967 -0.029271 0.003616 0.135820
embark_town 264.0 -0.006554 0.025388 -0.050465 -0.013431 -0.012491 -0.002658 0.062315
fare 264.0 0.022725 0.083313 -0.238293 -0.023252 -0.005715 0.054752 0.221681
n_siblings_spouses 264.0 0.002379 0.018807 -0.120684 0.002746 0.003192 0.005547 0.066210
parch 264.0 0.000140 0.003230 -0.052090 0.000235 0.000285 0.000497 0.000574
alone 264.0 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000

DFC の素晴らしい特性は寄与の合計 + bias が与えられたサンプルの予測に等しいことです。

# Sum of DFCs + bias == probabality.
bias = pred_dicts[0]['bias']
dfc_prob = df_dfc.sum(axis=1) + bias
np.testing.assert_almost_equal(dfc_prob.values,
                               probs.values)

個々の乗客のための DFC をプロットします。寄与の方向性に基づいたカラー・コーディングによりプロットを素晴らしくして図に特徴量値を追加しましょう。

# Boilerplate code for plotting : )
def _get_color(value):
    """To make positive DFCs plot green, negative DFCs plot red."""
    green, red = sns.color_palette()[2:4]
    if value >= 0: return green
    return red

def _add_feature_values(feature_values, ax):
    """Display feature's values on left of plot."""
    x_coord = ax.get_xlim()[0]
    OFFSET = 0.15
    for y_coord, (feat_name, feat_val) in enumerate(feature_values.items()):
        t = plt.text(x_coord, y_coord - OFFSET, '{}'.format(feat_val), size=12)
        t.set_bbox(dict(facecolor='white', alpha=0.5))
    from matplotlib.font_manager import FontProperties
    font = FontProperties()
    font.set_weight('bold')
    t = plt.text(x_coord, y_coord + 1 - OFFSET, 'feature\nvalue',
    fontproperties=font, size=12)
    
def plot_example(example):
  TOP_N = 8 # View top 8 features.
  sorted_ix = example.abs().sort_values()[-TOP_N:].index  # Sort by magnitude.
  example = example[sorted_ix]
  colors = example.map(_get_color).tolist()
  ax = example.to_frame().plot(kind='barh',
                          color=[colors],
                          legend=None,
                          alpha=0.75,
                          figsize=(10,6))
  ax.grid(False, axis='y')
  ax.set_yticklabels(ax.get_yticklabels(), size=14)

  # Add feature values.
  _add_feature_values(dfeval.iloc[ID][sorted_ix], ax)
  return ax
# Plot results.
ID = 182
example = df_dfc.iloc[ID]  # Choose ith example from evaluation set.
TOP_N = 8  # View top 8 features.
sorted_ix = example.abs().sort_values()[-TOP_N:].index
ax = plot_example(example)
ax.set_title('Feature contributions for example {}\n pred: {:1.2f}; label: {}'.format(ID, probs[ID], labels[ID]))
ax.set_xlabel('Contribution to predicted probability', size=14);

より大きな量の寄与がモデルの予測の上でより大きなインパクトを持ちます。ネガティブな寄与は与えられたサンプルに対する特徴量値がモデルの予測を減少させることを示し、その一方でポジティブな値は予測において増量に寄与します。

バイオリンプロットを使用して全体の分布と比較してサンプルの DFC をプロットすることもできます。

# Boilerplate plotting code.
def dist_violin_plot(df_dfc, ID):
  # Initialize plot.
  fig, ax = plt.subplots(1, 1, figsize=(10, 6))
  
  # Create example dataframe.
  TOP_N = 8  # View top 8 features.
  example = df_dfc.iloc[ID]
  ix = example.abs().sort_values()[-TOP_N:].index
  example = example[ix]
  example_df = example.to_frame(name='dfc')
  
  # Add contributions of entire distribution.
  parts=ax.violinplot([df_dfc[w] for w in ix],
                 vert=False,
                 showextrema=False,
                 widths=0.7,
                 positions=np.arange(len(ix)))
  face_color = sns_colors[0]
  alpha = 0.15
  for pc in parts['bodies']:
      pc.set_facecolor(face_color)
      pc.set_alpha(alpha)
  
  # Add feature values.
  _add_feature_values(dfeval.iloc[ID][sorted_ix], ax)

  # Add local contributions.
  ax.scatter(example,
              np.arange(example.shape[0]),
              color=sns.color_palette()[2],
              s=100,
              marker="s",
              label='contributions for example')
  
  # Legend
  # Proxy plot, to show violinplot dist on legend.
  ax.plot([0,0], [1,1], label='eval set contributions\ndistributions',
          color=face_color, alpha=alpha, linewidth=10)
  legend = ax.legend(loc='lower right', shadow=True, fontsize='x-large',
                     frameon=True)
  legend.get_frame().set_facecolor('white')
  
  # Format plot.
  ax.set_yticks(np.arange(example.shape[0]))
  ax.set_yticklabels(example.index)
  ax.grid(False, axis='y')
  ax.set_xlabel('Contribution to predicted probability', size=14)

このサンプルをプロットします。

dist_violin_plot(df_dfc, ID)
plt.title('Feature contributions for example {}\n pred: {:1.2f}; label: {}'.format(ID, probs[ID], labels[ID]));

最後に、LIMEshap のようなサードパーティのツールもまたモデルに対する個々の予測を理解する助けになれます。

 

グローバル特徴量重要度

追加として、個々の予測の研究よりもモデルを全体として理解することを望むかもしれません。下で、以下を計算して使用します :

  1. est.experimental_feature_importances を使用して gain-based 特徴量重要度
  2. 再配列 (= permutation) 重要度
  3. est.experimental_predict_with_explanations を使用して総計 DFC

gain-based 特徴量重要度は特定の特徴上で分割するときの損失変化を測定します、一方で再配列重要度は各特徴を一つずつシャッフルしてモデル性能の変化をシャッフルされた特徴に帰することによって検証セット上のモデル性能を評価することにより計算されます。

一般に、再配列特徴量重要度が gain-based 特徴量重要度より好まれます、両者のメソッドは潜在的予測変数が測定のスケールやカテゴリー数が変化する状況で特徴が相関関係があるときには信頼できない可能性はありますが (ソース)。異なる特徴量重要度タイプについての掘り下げた概要と素晴らしい議論については この記事 を調べてください。

 

1. Gain-based 特徴量重要度

grain-based 特徴量重要度は est.experimental_feature_importances を使用して TensorFlow Boosted Trees estimator に組み込まれます。

importances = est.experimental_feature_importances(normalize=True)
df_imp = pd.Series(importances)

# Visualize importances.
N = 8
ax = (df_imp.iloc[0:N][::-1]
    .plot(kind='barh',
          color=sns_colors[0],
          title='Gain feature importances',
          figsize=(10, 6)))
ax.grid(False, axis='y')

 

2. DFC の絶対値を平均する

グローバルレベルのインパクトを理解するために DFC の絶対値を平均することもできます。

# Plot.
dfc_mean = df_dfc.abs().mean()
N = 8
sorted_ix = dfc_mean.abs().sort_values()[-N:].index  # Average and sort by absolute.
ax = dfc_mean[sorted_ix].plot(kind='barh',
                       color=sns_colors[1],
                       title='Mean |directional feature contributions|',
                       figsize=(10, 6))
ax.grid(False, axis='y')

特徴量が変化するとき DFC がどのように変化するかを見ることもできます。

FEATURE = 'fare'
feature = pd.Series(df_dfc[FEATURE].values, index=dfeval[FEATURE].values).sort_index()
ax = sns.regplot(feature.index.values, feature.values, lowess=True);
ax.set_ylabel('contribution')
ax.set_xlabel(FEATURE);
ax.set_xlim(0, 100);

 

3. 再配列特徴量重要度

def permutation_importances(est, X_eval, y_eval, metric, features):
    """Column by column, shuffle values and observe effect on eval set.
    
    source: http://explained.ai/rf-importance/index.html
    A similar approach can be done during training. See "Drop-column importance"
    in the above article."""
    baseline = metric(est, X_eval, y_eval)
    imp = []
    for col in features:
        save = X_eval[col].copy()
        X_eval[col] = np.random.permutation(X_eval[col])
        m = metric(est, X_eval, y_eval)
        X_eval[col] = save
        imp.append(baseline - m)
    return np.array(imp)

def accuracy_metric(est, X, y):
    """TensorFlow estimator accuracy."""
    eval_input_fn = make_input_fn(X,
                                  y=y,
                                  shuffle=False,
                                  n_epochs=1)
    return est.evaluate(input_fn=eval_input_fn)['accuracy']
features = CATEGORICAL_COLUMNS + NUMERIC_COLUMNS
importances = permutation_importances(est, dfeval, y_eval, accuracy_metric,
                                      features)
df_imp = pd.Series(importances, index=features)

sorted_ix = df_imp.abs().sort_values().index
ax = df_imp[sorted_ix][-5:].plot(kind='barh', color=sns_colors[2], figsize=(10, 6))
ax.grid(False, axis='y')
ax.set_title('Permutation feature importance');

 

モデル fitting を可視化する

最初に次の式を使用して訓練データをシミュレート/ 作成しましょう :

\[
z=x* e^{-x^2 – y^2}
\]

ここで (z) は貴方が予測しようとする従属変数でそして (x) と (y) は特徴です。

from numpy.random import uniform, seed
from matplotlib.mlab import griddata

# Create fake data
seed(0)
npts = 5000
x = uniform(-2, 2, npts)
y = uniform(-2, 2, npts)
z = x*np.exp(-x**2 - y**2)
# Prep data for training.
df = pd.DataFrame({'x': x, 'y': y, 'z': z})

xi = np.linspace(-2.0, 2.0, 200),
yi = np.linspace(-2.1, 2.1, 210),
xi,yi = np.meshgrid(xi, yi);

df_predict = pd.DataFrame({
    'x' : xi.flatten(),
    'y' : yi.flatten(),
})
predict_shape = xi.shape
def plot_contour(x, y, z, **kwargs):
  # Grid the data.
  plt.figure(figsize=(10, 8))
  # Contour the gridded data, plotting dots at the nonuniform data points.
  CS = plt.contour(x, y, z, 15, linewidths=0.5, colors='k')
  CS = plt.contourf(x, y, z, 15,
                    vmax=abs(zi).max(), vmin=-abs(zi).max(), cmap='RdBu_r')
  plt.colorbar()  # Draw colorbar.
  # Plot data points.
  plt.xlim(-2, 2)
  plt.ylim(-2, 2)

関数をビジュアル化できます。より赤い色がより大きな関数値に対応します。

zi = griddata(x, y, z, xi, yi, interp='linear')
plot_contour(xi, yi, zi)
plt.scatter(df.x, df.y, marker='.')
plt.title('Contour on training data');

fc = [tf.feature_column.numeric_column('x'),
      tf.feature_column.numeric_column('y')]
def predict(est):
  """Predictions from a given estimator."""
  predict_input_fn = lambda: tf.data.Dataset.from_tensors(dict(df_predict))
  preds = np.array([p['predictions'][0] for p in est.predict(predict_input_fn)])
  return preds.reshape(predict_shape)

最初に線形モデルをデータに fit させてみましょう。

train_input_fn = make_input_fn(df, df.z)
est = tf.estimator.LinearRegressor(fc)
est.train(train_input_fn, max_steps=500);
plot_contour(xi, yi, predict(est))

それは非常に良い fit ではありません。

次に GBDT モデルをそれに fit させてみましょうそしてモデルがどのように関数に fit するか理解してみましょう。

n_trees = 22 #@param {type: "slider", min: 1, max: 80, step: 1}

est = tf.estimator.BoostedTreesRegressor(fc, n_batches_per_layer=1, n_trees=n_trees)
est.train(train_input_fn, max_steps=500)
clear_output()
plot_contour(xi, yi, predict(est))
plt.text(-1.8, 2.1, '# trees: {}'.format(n_trees), color='w', backgroundcolor='black', size=20);

木の数を増やすにつれて、モデルの予測は基礎となる関数をより良く近似します。

 

最後に

このチュートリアルでは DFC (directional feature contributions) と特徴量重要度テクニックを使用してブースティング木モデルをどのように解釈するかを学習しました。これらのテクニックは特徴量がモデルの予測にどのようにインパクトを与えるかの洞察を提供します。最後に、幾つかのモデルについて決定面を見ることによりブースティング木モデルが複雑な関数にどのように fit するかについての直感もまた得られました。

 

以上






TensorFlow 2.0 Alpha : Tutorials : Estimator :- TensorFlow 2.0 でブースティング木モデルをどのように訓練するか

TensorFlow 2.0 Alpha : Beginner Tutorials : Estimator :- TensorFlow 2.0 でブースティング木モデルをどのように訓練するか (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/03/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Estimator の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

Estimator :- TensorFlow 2.0 でブースティング木モデルをどのように訓練するか

このチュートリアルは tf.estimator API で決定木を使用する勾配ブースティング木を訓練する end-to-end なウォークスルーです。ブースティング木モデルは回帰と分類の両者のための最も一般的で効果的な機械学習アプローチの一つです。それは幾つか (10s, 100s あるいは 1000s さえも考えます) の木モデルからの予測を連結するアンサンブル・テクニックです。

ブースト木モデルは多くの機械学習実践者により人気があります、何故ならばそれらは最小限のハイパーパラメータ調整で素晴らしいパフォーマンスを獲得できるからです。

 

タイタニック・データセットをロードする

タイタニック・データセットを使用します、そこでは (寧ろ憂鬱な) 目標は性別、年齢、クラス, etc. のような特質が与えられたときに乗客の生存を予測することです。

from __future__ import absolute_import, division, print_function

import numpy as np
import pandas as pd
from IPython.display import clear_output

# Load dataset.
dftrain = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/train.csv')
dfeval = pd.read_csv('https://storage.googleapis.com/tf-datasets/titanic/eval.csv')
y_train = dftrain.pop('survived')
y_eval = dfeval.pop('survived')
!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
tf.random.set_seed(123)

データセットは訓練セットと評価セットから成ります :

  • dftrain と y_train は訓練セットです — モデルが学習するために使用するデータです。
  • モデルは評価セット, dfeval と y_eval に対してテストされます。

訓練のために次の特徴を使用します :

Feature Name Description
sex 乗客の性別
age 乗客の年齢
n_siblings_spouses # 乗船した兄弟とパートナー
parch # of 乗船した両親と子供
fare 乗客が支払った運賃
class 船の乗客のクラス
deck 乗客がどのデッキ上にいたか
embark_town 乗客がどの町から乗船したか
alone 乗客が一人であるか否か

 

データを調査する

最初にデータの幾つかをプレビューして訓練セット上の簡易統計 (= summary statistics) を作成します。

dftrain.head()

sex age n_siblings_spouses parch fare class deck embark_town alone
0 male 22.0 1 0 7.2500 Third unknown Southampton n
1 female 38.0 1 0 71.2833 First C Cherbourg n
2 female 26.0 0 0 7.9250 Third unknown Southampton y
3 female 35.0 1 0 53.1000 First C Southampton n
4 male 28.0 0 0 8.4583 Third unknown Queenstown y

dftrain.describe()

age n_siblings_spouses parch fare
count 627.000000 627.000000 627.000000 627.000000
mean 29.631308 0.545455 0.379585 34.385399
std 12.511818 1.151090 0.792999 54.597730
min 0.750000 0.000000 0.000000 0.000000
25% 23.000000 0.000000 0.000000 7.895800
50% 28.000000 0.000000 0.000000 15.045800
75% 35.000000 1.000000 0.000000 31.387500
max 80.000000 8.000000 5.000000 512.329200

訓練と評価セットにはそれぞれ 627 と 264 サンプルがあります。

dftrain.shape[0], dfeval.shape[0]
(627, 264)

乗客の大多数は 20 代と 30 代です。

dftrain.age.hist(bins=20);

乗船した男性の乗客は女性の乗客のおよそ 2 倍いました。

dftrain.sex.value_counts().plot(kind='barh');

乗客の多数は “third” クラスでした。

dftrain['class'].value_counts().plot(kind='barh');

殆どの乗客は Southampton から乗船しました。

dftrain['embark_town'].value_counts().plot(kind='barh');

女性は男性に比較して遥かに高い生存のチャンスを持ちます。これは明らかにモデルのための予測的な特徴となるでしょう。

pd.concat([dftrain, y_train], axis=1).groupby('sex').survived.mean().plot(kind='barh').set_xlabel('% survive');

 

feature column と入力関数を作成する

勾配ブースティング estimator は numeric と categorical features の両者を利用できます。feature columns は総ての TensorFlow estimator とともに動作してそれらの目的はモデリングのために使用される特徴を定義することです。更にそれらは one-hot-エンコーディング、正規化そして bucketization のようなある特徴エンジニアリング機能を提供します。このチュートリアルでは、CATEGORICAL_COLUMNS のフィールドは categorical columns から one-hot-エンコードされた columns (indicator column) へ変形されます :

fc = tf.feature_column
CATEGORICAL_COLUMNS = ['sex', 'n_siblings_spouses', 'parch', 'class', 'deck', 
                       'embark_town', 'alone']
NUMERIC_COLUMNS = ['age', 'fare']
  
def one_hot_cat_column(feature_name, vocab):
  return tf.feature_column.indicator_column(
      tf.feature_column.categorical_column_with_vocabulary_list(feature_name,
                                                 vocab))
feature_columns = []
for feature_name in CATEGORICAL_COLUMNS:
  # Need to one-hot encode categorical features.
  vocabulary = dftrain[feature_name].unique()
  feature_columns.append(one_hot_cat_column(feature_name, vocabulary))
  
for feature_name in NUMERIC_COLUMNS:
  feature_columns.append(tf.feature_column.numeric_column(feature_name,
                                           dtype=tf.float32))

feature column が生成する変形を見ることができます。例えば、ここに単一のサンプル上で indicator_column を使用するときの出力があります :

example = dict(dftrain.head(1))
class_fc = tf.feature_column.indicator_column(tf.feature_column.categorical_column_with_vocabulary_list('class', ('First', 'Second', 'Third')))
print('Feature value: "{}"'.format(example['class'].iloc[0]))
print('One-hot encoded: ', tf.keras.layers.DenseFeatures([class_fc])(example).numpy())
Feature value: "Third"
One-hot encoded:  [[ 0.  0.  1.]]

更に、総ての feature column 変形も一緒に見ることができます :

tf.keras.layers.DenseFeatures(feature_columns)(example).numpy()
array([[ 22.  ,   1.  ,   0.  ,   1.  ,   0.  ,   0.  ,   1.  ,   0.  ,
          0.  ,   0.  ,   0.  ,   0.  ,   0.  ,   0.  ,   1.  ,   0.  ,
          0.  ,   0.  ,   7.25,   1.  ,   0.  ,   0.  ,   0.  ,   0.  ,
          0.  ,   0.  ,   1.  ,   0.  ,   0.  ,   0.  ,   0.  ,   0.  ,
          1.  ,   0.  ]], dtype=float32)

次に入力関数を作成する必要があります。これらは訓練と推論の両者に対してデータがどのようにモデルに読まれるかを指定します。Pandas から直接データを読み込むために tf.data API の from_tensor_slices メソッドを使用します。これはより小さい、in-メモリなデータセットに対して適しています。より巨大なデータセットについては、メモリに収まらないデータセットを処理できるように tf.data API は (csv を含む) 様々な種類のファイルフォーマットをサポートします。

# Use entire batch since this is such a small dataset.
NUM_EXAMPLES = len(y_train)

def make_input_fn(X, y, n_epochs=None, shuffle=True):
  def input_fn():
    dataset = tf.data.Dataset.from_tensor_slices((dict(X), y))
    if shuffle:
      dataset = dataset.shuffle(NUM_EXAMPLES)
    # For training, cycle thru dataset as many times as need (n_epochs=None).    
    dataset = dataset.repeat(n_epochs)
    # In memory training doesn't use batching.
    dataset = dataset.batch(NUM_EXAMPLES)
    return dataset
  return input_fn

# Training and evaluation input functions.
train_input_fn = make_input_fn(dftrain, y_train)
eval_input_fn = make_input_fn(dfeval, y_eval, shuffle=False, n_epochs=1)

 

モデルを訓練して評価する

下では次のステップを行ないます :

  1. features とハイパーパラメータを指定して、モデルを初期化します。
  2. train_input_fn を使用してモデルに訓練データを供給して train 関数を使用してモデルを訓練します。
  3. 評価セット — この例では dfeval DataFrame を使用してモデル・パフォーマンスを評価します。予測が y_eval 配列からのラベルにマッチするか検証します。

ブースティング木モデルを訓練する前に、最初に線形分類器 (ロジスティック回帰モデル) を訓練しましょう。ベンチマークを確証するためにより単純なモデルで始めるのがベストプラクティスです。

linear_est = tf.estimator.LinearClassifier(feature_columns)

# Train model.
linear_est.train(train_input_fn, max_steps=100)

# Evaluation.
result = linear_est.evaluate(eval_input_fn)
clear_output()
print(pd.Series(result))
accuracy                  0.765152
accuracy_baseline         0.625000
auc                       0.832844
auc_precision_recall      0.789631
average_loss              0.478908
global_step             100.000000
label/mean                0.375000
loss                      0.478908
precision                 0.703297
prediction/mean           0.350790
recall                    0.646465
dtype: float64

次に、ブーティング木モデルを訓練しましょう。ブースティング木については、回帰 (BoostedTreesRegressor) と分類 (BoostedTreesClassifier) がサポートされます。目標はクラス – 生存か非生存かを推測することですから、BoostedTreesClassifier を使用します。

# Since data fits into memory, use entire dataset per layer. It will be faster.
# Above one batch is defined as the entire dataset. 
n_batches = 1
est = tf.estimator.BoostedTreesClassifier(feature_columns,
                                          n_batches_per_layer=n_batches)

# The model will stop training once the specified number of trees is built, not 
# based on the number of steps.
est.train(train_input_fn, max_steps=100)

# Eval.
result = est.evaluate(eval_input_fn)
clear_output()
print(pd.Series(result))
accuracy                  0.829545
accuracy_baseline         0.625000
auc                       0.872788
auc_precision_recall      0.857807
average_loss              0.411839
global_step             100.000000
label/mean                0.375000
loss                      0.411839
precision                 0.793478
prediction/mean           0.381942
recall                    0.737374
dtype: float64

今では評価セットからの乗客について予測を行なうために訓練モデルを使用できます。TensorFlow モデルはサンプルのバッチやコレクションの上で同時に予測を行なうために最適化されています。前に、eval_input_fn は評価セット全体を使用して定義されています。

pred_dicts = list(est.predict(eval_input_fn))
probs = pd.Series([pred['probabilities'][1] for pred in pred_dicts])

probs.plot(kind='hist', bins=20, title='predicted probabilities');

最後に結果の ROC (receiver operating characteristic) もまた見ることができます、これは真陽性率 (= true positive rate) と偽陽性率 (= false positive rate) 間のトレードオフのより良い考えを与えます。

from sklearn.metrics import roc_curve
from matplotlib import pyplot as plt

fpr, tpr, _ = roc_curve(y_eval, probs)
plt.plot(fpr, tpr)
plt.title('ROC curve')
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.xlim(0,)
plt.ylim(0,);

 

以上



TensorFlow 2.0 Alpha : Tutorials : テキストとシークエンス :- RNN でテキスト分類

TensorFlow 2.0 Alpha : Beginner Tutorials : テキストとシークエンス :- RNN でテキスト分類 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/02/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- RNN でテキスト分類

このテキスト分類チュートリアルはセンチメント分析のために IMDB 巨大映画レビューデータセット 上で リカレント・ニューラルネットワーク を訓練します。

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow_datasets as tfds
import tensorflow as tf

matplotlib をインポートしてグラフをプロットするためのヘルパー関数を作成します :

import matplotlib.pyplot as plt


def plot_graphs(history, string):
  plt.plot(history.history[string])
  plt.plot(history.history['val_'+string])
  plt.xlabel("Epochs")
  plt.ylabel(string)
  plt.legend([string, 'val_'+string])
  plt.show()

 

入力パイプラインをセットアップする

IMDB 巨大映画レビューデータセットは二値分類データセットです — 総てのレビューはポジティブかネガティブなセンチメントを持ちます。

TFDS を使用してデータセットをダウンロードします。データセットは作り付けの部分語字句解析器 (= subword tokenizer) を装備しています。

dataset, info = tfds.load('imdb_reviews/subwords8k', with_info=True, 
                          as_supervised=True)
train_dataset, test_dataset = dataset['train'], dataset['test']

これは部分語字句解析器ですから、それは任意の文字列を渡すことができて字句解析器はそれをトークン化します。

tokenizer = info.features['text'].encoder
print ('Vocabulary size: {}'.format(tokenizer.vocab_size))
Vocabulary size: 8185
sample_string = 'TensorFlow is cool.'

tokenized_string = tokenizer.encode(sample_string)
print ('Tokenized string is {}'.format(tokenized_string))

original_string = tokenizer.decode(tokenized_string)
print ('The original string: {}'.format(original_string))

assert original_string == sample_string
Tokenized string is [6307, 2327, 4043, 4265, 9, 2724, 7975]
The original string: TensorFlow is cool.

字句解析器は単語がその辞書にない場合には文字列を部分語に分解してエンコードします。

for ts in tokenized_string:
  print ('{} ----> {}'.format(ts, tokenizer.decode([ts])))
6307 ----> Ten
2327 ----> sor
4043 ----> Fl
4265 ----> ow 
9 ----> is 
2724 ----> cool
7975 ----> .
BUFFER_SIZE = 10000
BATCH_SIZE = 64
train_dataset = train_dataset.shuffle(BUFFER_SIZE)
train_dataset = train_dataset.padded_batch(BATCH_SIZE, train_dataset.output_shapes)

test_dataset = test_dataset.padded_batch(BATCH_SIZE, test_dataset.output_shapes)

 

モデルを作成する

tf.keras.Sequential モデルを構築して埋め込み層から始めます。埋め込み層は単語毎に一つのベクトルをストアします。呼び出されたとき、それは単語インデックスのシークエンスをベクトルのシークエンスに変換します。これらのベクトルは訓練可能です。(十分なデータ上で) 訓練後、類似の意味を持つ単語はしばしば同様のベクトルを持ちます。

このインデックス-検索は tf.keras.layers.Dense 層を通した one-hot エンコード・ベクトルを渡す等値の演算よりも遥かにより効率的です。

リカレント・ニューラルネットワーク (RNN) は要素を通した iterate によるシークエンス入力を処理します。RNN は一つのタイムスタンプからの出力をそれらの入力 — そして次へと渡します。

tf.keras.layers.Bidirectional ラッパーはまた RNN 層とともに使用できます。これは RNN 層を通して入力を foward そして backward に伝播してそれから出力を連結します。これは RNN が長期の (= long range) 依存性を学習する手助けをします。

model = tf.keras.Sequential([
    tf.keras.layers.Embedding(tokenizer.vocab_size, 64),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

訓練プロセスを構成するために Keras モデルをコンパイルします :

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

 

モデルを訓練する

history = model.fit(train_dataset, epochs=10, 
                    validation_data=test_dataset)
Epoch 1/10
391/391 [==============================] - 75s 191ms/step - loss: 0.5536 - accuracy: 0.7140 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
391/391 [==============================] - 73s 187ms/step - loss: 0.3922 - accuracy: 0.8311 - val_loss: 0.5141 - val_accuracy: 0.7940
Epoch 3/10
391/391 [==============================] - 71s 182ms/step - loss: 0.3120 - accuracy: 0.8807 - val_loss: 0.4517 - val_accuracy: 0.8098
Epoch 4/10
391/391 [==============================] - 78s 199ms/step - loss: 0.2548 - accuracy: 0.9030 - val_loss: 0.4383 - val_accuracy: 0.8235
Epoch 5/10
391/391 [==============================] - 72s 185ms/step - loss: 0.2387 - accuracy: 0.9078 - val_loss: 0.4918 - val_accuracy: 0.8214
Epoch 6/10
391/391 [==============================] - 71s 182ms/step - loss: 0.1905 - accuracy: 0.9293 - val_loss: 0.4849 - val_accuracy: 0.8162
Epoch 7/10
391/391 [==============================] - 71s 182ms/step - loss: 0.1900 - accuracy: 0.9282 - val_loss: 0.5919 - val_accuracy: 0.8257
Epoch 8/10
391/391 [==============================] - 74s 190ms/step - loss: 0.1321 - accuracy: 0.9526 - val_loss: 0.6331 - val_accuracy: 0.7657
Epoch 9/10
391/391 [==============================] - 73s 187ms/step - loss: 0.3290 - accuracy: 0.8516 - val_loss: 0.6709 - val_accuracy: 0.6501
Epoch 10/10
391/391 [==============================] - 70s 180ms/step - loss: 0.3074 - accuracy: 0.8692 - val_loss: 0.5533 - val_accuracy: 0.7873
test_loss, test_acc = model.evaluate(test_dataset)

print('Test Loss: {}'.format(test_loss))
print('Test Accuracy: {}'.format(test_acc))
    391/Unknown - 19s 47ms/step - loss: 0.5533 - accuracy: 0.7873Test Loss: 0.553319326714
Test Accuracy: 0.787320017815

上のモデルはシークエンスに適用されるパディングをマスクしていません。これはパッドされたシークエンス上で訓練してパッドされていないシークエンス上でテストする場合に歪みに繋がる可能性があります。理想的にはモデルはパディングを無視することを学習するでしょうが、下で見れるようにそれは出力上で小さい効果を持つだけです。

prediction が >= 0.5 であれば、それはポジティブでそうでなければネガティブです。

def pad_to_size(vec, size):
  zeros = [0] * (size - len(vec))
  vec.extend(zeros)
  return vec
def sample_predict(sentence, pad):
  tokenized_sample_pred_text = tokenizer.encode(sample_pred_text)
  
  if pad:
    tokenized_sample_pred_text = pad_to_size(tokenized_sample_pred_text, 64)
  
  predictions = model.predict(tf.expand_dims(tokenized_sample_pred_text, 0))

  return (predictions)
# predict on a sample text without padding.

sample_pred_text = ('The movie was cool. The animation and the graphics '
                    'were out of this world. I would recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=False)
print (predictions)
[[ 0.68914342]]
# predict on a sample text with padding

sample_pred_text = ('The movie was cool. The animation and the graphics '
                    'were out of this world. I would recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=True)
print (predictions)
[[ 0.68634349]]
plot_graphs(history, 'accuracy')

plot_graphs(history, 'loss')

 

2 つあるいはそれ以上の LSTM 層をスタックする

Keras リカレント層は return_sequences コンストラクタ引数で制御される 2 つの利用可能なモードを持ちます :

  • 各タイムスタンプのための連続する出力の完全なシークエンスを返すか (shape (batch_size, timesteps, output_features) の 3D tensor)、
  • 各入力シークエンスのための最後の出力だけを返します (shape (batch_size, output_features) の 2D tensor)。
model = tf.keras.Sequential([
    tf.keras.layers.Embedding(tokenizer.vocab_size, 64),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(
        64, return_sequences=True)),
    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(32)),
    tf.keras.layers.Dense(64, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])
model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
history = model.fit(train_dataset, epochs=10, 
                    validation_data=test_dataset)
Epoch 1/10
391/391 [==============================] - 155s 397ms/step - loss: 0.6349 - accuracy: 0.6162 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00
Epoch 2/10
391/391 [==============================] - 155s 396ms/step - loss: 0.6333 - accuracy: 0.6134 - val_loss: 0.5872 - val_accuracy: 0.6914
Epoch 3/10
391/391 [==============================] - 153s 391ms/step - loss: 0.4199 - accuracy: 0.8217 - val_loss: 0.4361 - val_accuracy: 0.8187
Epoch 4/10
391/391 [==============================] - 156s 398ms/step - loss: 0.3088 - accuracy: 0.8785 - val_loss: 0.4131 - val_accuracy: 0.8319
Epoch 5/10
391/391 [==============================] - 153s 391ms/step - loss: 0.3328 - accuracy: 0.8564 - val_loss: 0.4689 - val_accuracy: 0.7958
Epoch 6/10
391/391 [==============================] - 156s 398ms/step - loss: 0.2383 - accuracy: 0.9128 - val_loss: 0.4299 - val_accuracy: 0.8404
Epoch 7/10
391/391 [==============================] - 152s 388ms/step - loss: 0.2426 - accuracy: 0.9039 - val_loss: 0.4934 - val_accuracy: 0.8299
Epoch 8/10
391/391 [==============================] - 155s 396ms/step - loss: 0.1638 - accuracy: 0.9440 - val_loss: 0.5106 - val_accuracy: 0.8279
Epoch 9/10
391/391 [==============================] - 150s 383ms/step - loss: 0.1616 - accuracy: 0.9420 - val_loss: 0.5287 - val_accuracy: 0.8245
Epoch 10/10
391/391 [==============================] - 154s 394ms/step - loss: 0.1120 - accuracy: 0.9643 - val_loss: 0.5646 - val_accuracy: 0.8070
test_loss, test_acc = model.evaluate(test_dataset)

print('Test Loss: {}'.format(test_loss))
print('Test Accuracy: {}'.format(test_acc))
    391/Unknown - 45s 115ms/step - loss: 0.5646 - accuracy: 0.8070Test Loss: 0.564571284348
Test Accuracy: 0.80703997612
# predict on a sample text without padding.

sample_pred_text = ('The movie was not good. The animation and the graphics '
                    'were terrible. I would not recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=False)
print (predictions)
[[ 0.00393916]]
# predict on a sample text with padding

sample_pred_text = ('The movie was not good. The animation and the graphics '
                    'were terrible. I would not recommend this movie.')
predictions = sample_predict(sample_pred_text, pad=True)
print (predictions)
[[ 0.01098633]]
plot_graphs(history, 'accuracy')

plot_graphs(history, 'loss')

GRU 層 のような他の存在するリカレント層を調べてください。

 

以上



TensorFlow 2.0 Alpha : Tutorials : テキストとシークエンス :- 単語埋め込み

TensorFlow 2.0 Alpha : Beginner Tutorials : テキストとシークエンス :- 単語埋め込み (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

テキストとシークエンス :- 単語埋め込み

このチュートリアルは単語埋め込みを紹介します。それは小さいデータセット上でスクラッチから単語埋め込みを訓練して埋め込みプロジェクター (= Embedding Projector) (下の画像で示されます) を使用してこれらの埋め込みを可視化する完全なコードを含みます。

 

テキストを数字として表現する

機械学習モデルは入力としてベクトル (数字の配列) を取ります。テキストで作業するとき、行わなければならない最初のことはそれをモデルに供給する前の文字列を数字に変換する (or テキストを「ベクトル化」する) ストラテジーとともに提示されます。このセクションでは、それを行なうための 3 つのストラテジーを見ます。

 

One-hot エンコーディング

最初のアイデアとして、語彙の各単語を “one-hot” エンコードできるかもしれません。センテンス “The cat sat on the mat” を考えます。このセンテンスの語彙 (or 一意の単語) は (cat, mat, on, sat, the) です。各単語を表わすために、語彙に等しい長さを持つゼロベクトルを作成してから、単語に対応するインデックス内に 1 を置きます。このアプローチは次の図で示されます。

センテンスのエンコーディングを含むベクトルを作成するために、それから各単語のための one-hot ベクトルを連結できるでしょう。

Key point: このアプローチは不十分です。one-hot エンコード・ベクトルは疎です (つまり、殆どのインデックスがゼロです)。語彙に 10,000 単語を持つことを想像してください。各単語を one-hot エンコードするには、要素の 99.99% はゼロであるベクトルを作成するでしょう。

 

一意の数字を持つ各単語をエンコードする

私達が試すかもしれない 2 番目のアプローチは一意の数字を使用して各単語をエンコードすることです。上の例を続けるのであれば、1 を “cat” に、2 を “mat” 等に割り当てることができるでしょう。それからセンテンス “The cat sat on the mat” を [5, 1, 4, 3, 5, 2] のような密ベクトルとしてエンコードできるでしょう。このアプローチは効率的です。疎ベクトルの代わりに、今では密なひとつ (そこでは総ての要素は満ちています) を持ちます。

けれども、このアプローチには 2 つの欠点があります :

  • 整数エンコーディングは恣意的 (= arbitrary) です (それは単語間のどのような関係も捕捉しません)。
  • 整数エンコーディングはモデルが解釈することは挑戦的です。例えば、線形分類器は各特徴について単一の重みを学習します。異なる単語は同様のエンコーディングを持つかもしれませんので、この特徴-重みの組み合わせは意味がありません。

 

単語埋め込み

単語埋め込みは効率的で、密な表現を使用する方法を与えてくれます、そこでは類似した単語は類似したエンコーディングを持ちます。重要なことは、このエンコーディングを手動で指定しなくてかまわないことです。埋め込みは浮動小数点値の密ベクトルです (ベクトルの長さは貴方が指定するパラメータです)。埋め込みのための値を手動で指定する代わりに、それらは訓練可能なパラメータです (モデルが dense 層のための重みを学習するのと同じ方法で、訓練の間にモデルにより学習される重みです)。(小さいデータセットのための) 8-次元から、巨大なデータセットで作業するとき 1024 次元までの単語埋め込みを見ることは一般的です。高い次元の埋め込みは単語間のきめ細かい関係性を捕捉できますが、学習するためにより多くのデータを取ります。

上は単語埋め込みのための図です。各単語は浮動小数点値の 4-次元ベクトルとして表現されます。埋め込みを考えるもう一つの方法は「検索テーブル (= lookup table)」です。これらの重みが学習された後、各単語をそれがテーブルで対応する密ベクトルを検索することでエンコードできます。

 

埋め込み層を使用する

Keras は単語埋め込みを使用することを容易にします。Embedding 層を見てみましょう。

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

# The Embedding layer takes at least two arguments:
# the number of possible words in the vocabulary, here 1000 (1 + maximum word index),
# and the dimensionality of the embeddings, here 32.
embedding_layer = layers.Embedding(1000, 32)

Embedding 層は整数インデックス (これは特定の単語を表します) から密ベクトル (それらの埋め込み) へマップする検索テーブルとして理解できます。埋め込みの次元は、(Dense 層のニューロン数で実験するのとだいたい同じ方法で) それで貴方の問題のために何が上手く動作するかを見るための実験可能なパラメータです。

埋め込み層を作成するとき、その埋め込めのための重みは (丁度任意の他の層のように) ランダムに初期化されます。訓練の間に、それらは徐々に逆伝播で調整されます。ひとたび訓練されれば、学習された単語埋め込みは単語間の類似性を大雑把にエンコードします (何故ならばそれらは貴方のモデルがその上で訓練された特定の問題のために学習されたからです)。

入力として、Embedding 層は shape (samples, sequence_length) の整数の 2D tensor を取ります、そこでは各エントリは整数のシークエンスです。それは可変長のシークエンスを埋め込むことができます。上の埋め込み層に shapes (32, 10) (長さ 10 の 32 シークエンスのバッチ) あるいは (64, 15) (長さ 15 の 64 シークエンスのバッチ) を持つバッチを供給できるでしょう。バッチの総てのシークエンスは同じ長さを持たなければなりませんので、他よりも短いシークエンスはゼロでパディングされるべきで、より長いシークエンスは切り縮められるべきです。

出力として、埋め込み層は shape (samples, sequence_length, embedding_dimensionality) の 3D 浮動小数点 tensor を返します、そのような 3D tensor はそれから RNN で処理できますし、あるいは単純に平坦化されるかプールされるかして Dense 層で処理できます。このチュートリアルでは後者のアプローチを示します、そして前者を学習するために Text Classification with an RNN を参照できます。

 

スクラッチから埋め込みを学習する

IMDB 映画レビュー上でセンチメント分類器を訓練します。この過程で、スクラッチから埋め込みを学習します。データセットをダウンロードして前処理するコードを通して迅速に進めます (より詳細はこの チュートリアル を見てください)。

vocab_size = 10000
imdb = keras.datasets.imdb
(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=vocab_size)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step

インポートされたとき、レビューのテキストは整数エンコードされています (各整数は辞書の特定の単語を表します)。

print(train_data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]

 

整数を単語に変換し戻す

整数群をどのようにテキストに変換し戻すかを知ることは有用かもしれません。ここで、整数から文字列へのマッピングを含む辞書オブジェクトに問い合わせるためのヘルパー関数を作成します :

# A dictionary mapping words to an integer index
word_index = imdb.get_word_index()

# The first indices are reserved
word_index = {k:(v+3) for k,v in word_index.items()} 
word_index[""] = 0
word_index[""] = 1
word_index[""] = 2  # unknown
word_index[""] = 3

reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])

def decode_review(text):
    return ' '.join([reverse_word_index.get(i, '?') for i in text])
  
decode_review(train_data[0])
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb_word_index.json
1646592/1641221 [==============================] - 0s 0us/step

"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"

映画レビューは異なる長さであり得ます。レビューの長さを標準化するために pad_sequences 関数を使用します。

maxlen = 500

train_data = keras.preprocessing.sequence.pad_sequences(train_data,
                                                        value=word_index[""],
                                                        padding='post',
                                                        maxlen=maxlen)

test_data = keras.preprocessing.sequence.pad_sequences(test_data,
                                                       value=word_index[""],
                                                       padding='post',
                                                       maxlen=maxlen)

最初のパッドされたレビューを調べましょう。

print(train_data[0])
[   1   14   22   16   43  530  973 1622 1385   65  458 4468   66 3941
    4  173   36  256    5   25  100   43  838  112   50  670    2    9
   35  480  284    5  150    4  172  112  167    2  336  385   39    4
  172 4536 1111   17  546   38   13  447    4  192   50   16    6  147
 2025   19   14   22    4 1920 4613  469    4   22   71   87   12   16
   43  530   38   76   15   13 1247    4   22   17  515   17   12   16
  626   18    2    5   62  386   12    8  316    8  106    5    4 2223
 5244   16  480   66 3785   33    4  130   12   16   38  619    5   25
  124   51   36  135   48   25 1415   33    6   22   12  215   28   77
   52    5   14  407   16   82    2    8    4  107  117 5952   15  256
    4    2    7 3766    5  723   36   71   43  530  476   26  400  317
   46    7    4    2 1029   13  104   88    4  381   15  297   98   32
 2071   56   26  141    6  194 7486   18    4  226   22   21  134  476
   26  480    5  144   30 5535   18   51   36   28  224   92   25  104
    4  226   65   16   38 1334   88   12   16  283    5   16 4472  113
  103   32   15   16 5345   19  178   32    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0    0    0    0    0
    0    0    0    0    0    0    0    0    0    0]

 

単純なモデルを作成する

モデルを定義するために Keras Sequential API を使用します。

  • 最初の層は Embedding 層です。この層は整数エンコードされた語彙を取り各単語インデックスのための埋め込みベクトルを検索します。これらのベクトルはモデルを訓練するときに学習されます。ベクトルは出力配列に次元を追加します。結果としての次元は : (batch, sequence, embedding) です。
  • 次に、GlobalAveragePooling1D 層はシークエンス次元に渡り平均することにより各サンプルのために固定長出力ベクトルを返します。これは可能な最も単純な方法の中で、モデルに可変長の入力を処理することを可能にします。
  • この固定長出力ベクトルが 16 隠れユニットを持つ完全結合 (Dense) 層を通してパイプされます。
  • 最後の層は単一出力ノードと密に結合されます。sigmoid 活性化関数を使用して、この値は 0 と 1 の間の float で、レビューがポジティブである確率 (or 確信度レベル) を表します。
embedding_dim=16

model = keras.Sequential([
  layers.Embedding(vocab_size, embedding_dim, input_length=maxlen),
  layers.GlobalAveragePooling1D(),
  layers.Dense(16, activation='relu'),
  layers.Dense(1, activation='sigmoid')
])

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding_1 (Embedding)      (None, 500, 16)           160000    
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 16)                272       
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,289
Trainable params: 160,289
Non-trainable params: 0
_________________________________________________________________

 

モデルをコンパイルして訓練する

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

history = model.fit(
    train_data,
    train_labels,
    epochs=30,
    batch_size=512,
    validation_split=0.2)
Train on 20000 samples, validate on 5000 samples
Epoch 1/30
20000/20000 [==============================] - 2s 97us/sample - loss: 0.6927 - accuracy: 0.5265 - val_loss: 0.6921 - val_accuracy: 0.5212
Epoch 2/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.6904 - accuracy: 0.5512 - val_loss: 0.6877 - val_accuracy: 0.5928
Epoch 3/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.6833 - accuracy: 0.6811 - val_loss: 0.6766 - val_accuracy: 0.7204
Epoch 4/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.6668 - accuracy: 0.7427 - val_loss: 0.6528 - val_accuracy: 0.7448
Epoch 5/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.6362 - accuracy: 0.7778 - val_loss: 0.6148 - val_accuracy: 0.7808
Epoch 6/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.5908 - accuracy: 0.8000 - val_loss: 0.5655 - val_accuracy: 0.7986
Epoch 7/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.5377 - accuracy: 0.8174 - val_loss: 0.5157 - val_accuracy: 0.8146
Epoch 8/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.4838 - accuracy: 0.8405 - val_loss: 0.4699 - val_accuracy: 0.8278
Epoch 9/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.4359 - accuracy: 0.8578 - val_loss: 0.4306 - val_accuracy: 0.8492
Epoch 10/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.3950 - accuracy: 0.8717 - val_loss: 0.4005 - val_accuracy: 0.8586
Epoch 11/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.3621 - accuracy: 0.8792 - val_loss: 0.3767 - val_accuracy: 0.8646
Epoch 12/30
20000/20000 [==============================] - 2s 81us/sample - loss: 0.3358 - accuracy: 0.8856 - val_loss: 0.3590 - val_accuracy: 0.8682
Epoch 13/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.3138 - accuracy: 0.8929 - val_loss: 0.3447 - val_accuracy: 0.8712
Epoch 14/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.2952 - accuracy: 0.8989 - val_loss: 0.3335 - val_accuracy: 0.8724
Epoch 15/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2808 - accuracy: 0.9036 - val_loss: 0.3245 - val_accuracy: 0.8766
Epoch 16/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2667 - accuracy: 0.9077 - val_loss: 0.3235 - val_accuracy: 0.8736
Epoch 17/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.2546 - accuracy: 0.9114 - val_loss: 0.3115 - val_accuracy: 0.8808
Epoch 18/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.2431 - accuracy: 0.9160 - val_loss: 0.3052 - val_accuracy: 0.8822
Epoch 19/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2332 - accuracy: 0.9191 - val_loss: 0.3012 - val_accuracy: 0.8846
Epoch 20/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2240 - accuracy: 0.9219 - val_loss: 0.2976 - val_accuracy: 0.8868
Epoch 21/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2163 - accuracy: 0.9247 - val_loss: 0.2944 - val_accuracy: 0.8884
Epoch 22/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.2101 - accuracy: 0.9267 - val_loss: 0.2919 - val_accuracy: 0.8888
Epoch 23/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.2018 - accuracy: 0.9304 - val_loss: 0.2915 - val_accuracy: 0.8882
Epoch 24/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.1949 - accuracy: 0.9323 - val_loss: 0.2886 - val_accuracy: 0.8910
Epoch 25/30
20000/20000 [==============================] - 2s 84us/sample - loss: 0.1884 - accuracy: 0.9342 - val_loss: 0.2886 - val_accuracy: 0.8904
Epoch 26/30
20000/20000 [==============================] - 2s 83us/sample - loss: 0.1835 - accuracy: 0.9360 - val_loss: 0.2867 - val_accuracy: 0.8918
Epoch 27/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.1778 - accuracy: 0.9395 - val_loss: 0.2870 - val_accuracy: 0.8910
Epoch 28/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.1720 - accuracy: 0.9413 - val_loss: 0.2851 - val_accuracy: 0.8904
Epoch 29/30
20000/20000 [==============================] - 2s 82us/sample - loss: 0.1665 - accuracy: 0.9439 - val_loss: 0.2845 - val_accuracy: 0.8930
Epoch 30/30
20000/20000 [==============================] - 2s 81us/sample - loss: 0.1618 - accuracy: 0.9453 - val_loss: 0.2869 - val_accuracy: 0.8930

このアプローチで私達のモデルは 88% まわりの検証精度に到達します (モデルは overfitting し、訓練精度はかなり高いです)。

import matplotlib.pyplot as plt

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

epochs = range(1, len(acc) + 1)

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.figure(figsize=(16,9))

plt.show()
<Figure size 640x480 with 1 Axes>
<Figure size 1600x900 with 0 Axes>

 

学習された埋め込みを取得する

次に、訓練の間に学習された単語埋め込みを取得しましょう。これは shape (vocab_size,embedding-dimension) の行列です。

e = model.layers[0]
weights = e.get_weights()[0]
print(weights.shape) # shape: (vocab_size, embedding_dim)
(10000, 16)

さて重みをディスクに書きます。Embedding Projector を使用するには、タブ区切りフォーマットで 2 つのファイルをアップロードします : (埋め込みを含む) ベクトルのファイル、そして (単語を含む) メタデータのファイルです。

out_v = open('vecs.tsv', 'w')
out_m = open('meta.tsv', 'w')
for word_num in range(vocab_size):
  word = reverse_word_index[word_num]
  embeddings = weights[word_num]
  out_m.write(word + "\n")
  out_v.write('\t'.join([str(x) for x in embeddings]) + "\n")
out_v.close()
out_m.close()

このチュートリアルを Colaboratory で実行している場合、これらのファイルを貴方のローカルマシンにダウンロードするために次のスニペットを使用できます (or ファイルブラウザを使用してください、View -> Table of contents -> File browser)。

# from google.colab import files
# files.download('vecs.tsv')
# files.download('meta.tsv')

 

埋め込みを可視化する

私達の埋め込みを可視化するには、それらを embedding projector にアップロードします。

Embedding Projector をオープンします。

  • “Load data” をクリックする
  • 上で作成された 2 つのファイルをアップロードする : vecs.tsv と meta.tsv.

貴方が訓練した埋め込みが今表示されます。単語をそれらに最も近い近傍を見つけるために検索することができます。例えば、”beautiful” を検索してみてください。”wonderful” のような近傍を見るかもしれません。Note: 埋め込み層を訓練する前に重みがどのようにランダムに初期化されたかに依拠して、貴方の結果は少し異なるかもしれません。

Note: 実験的に、より単純なモデルを使用してより解釈可能な埋め込みを生成できるかもしれません。Dense(16) 層を削除して、モデルを再訓練し、そして埋め込みを再度可視化してみてください。

 

Next steps

このチュートリアルは小さいデータセット上でスクラッチからどのように単語埋め込みを訓練して可視化するかを示しました。

  • Kreas で埋め込みについて更に学習するためには、François Chollet のこれらの ノートブック を勧めます。
  • テキスト分類について更に学習するためには (全体的なワークフローを含み、そしてもし貴方がいつ埋め込み vs one-hot エンコーディングを使用するかについて関心があれば)、この実践的なテキスト分類 ガイド を勧めます。
 

以上



TensorFlow 2.0 Alpha : Tutorials : 画像 :- 事前訓練された ConvNet を使用する転移学習

TensorFlow 2.0 Alpha : Beginner Tutorials : 画像 :- 事前訓練された ConvNet を使用する転移学習 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

画像 :- 事前訓練された ConvNet を使用する転移学習

このチュートリアルでは事前訓練されたネットワークからの転移学習を使用して猫 vs 犬画像をどのように分類するかを議論します。これはスクラッチからネットワークを訓練することによって見たものより高い精度を得ることを可能にします。

**事前訓練されたモデル** は、典型的には巨大スケール画像分類タスク上の、巨大なデータセット上で以前に訓練された (セーブされた) ネットワークです。事前訓練されたモデルをそのまま使用するか事前訓練された convnet を使用した学習を転移させることができます。**転移学習** の背後にある直感は、このモデルが十分に巨大で一般的なデータセット上で訓練された場合、このモデルは視覚世界の一般的なモデルとして効果的に役立つであろうということです。これらのモデルを私達のタスクに固有の私達自身のモデルの基盤として使用することで巨大なデータセット上で巨大なモデルを訓練しなくてもこれらの学習された特徴マップを活用できます。事前訓練されたモデルを使用する転移学習の 2 つのシナリオがあります :

  1. 特徴抽出 (= Feature Extraction) – 新しいサンプルから意味がある特徴を抽出するために以前のネットワークにより学習された表現を使用します。事前訓練されたモデルの上に (スクラッチから訓練される) 新しい分類器を単に追加します、その結果前に学習された特徴マップを私達のデータセットのために最目的化できます。事前訓練されたモデル全体か単に畳み込みベースを使用しますか? – これらの事前訓練された convnet (畳み込みベース) の特徴抽出部分を使用します、何故ならばそれらは一般的な特徴で写真に渡り学習された概念でありがちだからです。けれども、事前訓練されたモデルの分類パートはしばしば元の分類タスクに特有で、結果的にモデルが訓練された上のクラスのセットに固有です。
  2. 再調整 (= Fine-Tuning) – 特徴抽出のために使用される凍結されたモデルベースの 2, 3 の top 層を解凍して、凍結モデルの最後の層群に加えて新たに追加された分類層の両者を連帯して訓練します。これは最後の分類器に加えて高次特徴表現を、関係する特定のタスクのためにより関連させるために「再調整」することを可能にします。

一般的な機械学習ワークフローに従います : 1. データを調べて理解する。2. 入力パイプラインを構築する – 画像分類チュートリアルで行なったように Keras ImageDataGenerator を使用する。3. モデルを構成する * 事前訓練されたモデル (と事前訓練された重み) をロードする * top に分類層をスタックする。4. モデルを訓練する。5. モデルを評価する。

事前訓練された convnet を特徴抽出として使用する例を見てからベースモデルの最後の 2, 3 層を訓練するために再調整します。

from __future__ import absolute_import, division, print_function

import os

import numpy as np

import matplotlib.pyplot as plt
!pip install -q tensorflow-gpu==2.0.0-alpha0
import tensorflow as tf

keras = tf.keras
Collecting tensorflow-gpu==2.0.0-alpha0
Successfully installed google-pasta-0.1.4 tb-nightly-1.14.0a20190303 tensorflow-estimator-2.0-preview-1.14.0.dev2019030300 tensorflow-gpu==2.0.0-alpha0-2.0.0.dev20190303

 

データ前処理

データ・ダウンロード

猫と犬のデータセットをロードするために TensorFlow Dataset を利用します。

この tfds パッケージは事前定義されたデータをロードするための最も容易な方法です。もし貴方自身のデータを持ち、インポートしてそれを TensorFlow で使用することに興味がれば loading image data を見てください。

import tensorflow_datasets as tfds

tfds.load メソッドはデータをダウンロードしてキャッシュし、tf.data.Dataset オブジェクトを返します。これらのオブジェクトはデータを操作してそれをモデルにパイプするためのパワフルで、効率的なメソッドを提供します。

“cats_vs_dog” は標準的な分割を定義していないので、それをデータの 80%, 10%, 10% で (train, validation, test) にそれぞれ分割するために subsplit 機能を使用します。

SPLIT_WEIGHTS = (8, 1, 1)
splits = tfds.Split.TRAIN.subsplit(weighted=SPLIT_WEIGHTS)

(raw_train, raw_validation, raw_test), metadata = tfds.load(
    'cats_vs_dogs', split=list(splits),
    with_info=True, as_supervised=True)
Downloading / extracting dataset cats_vs_dogs (786.68 MiB) to /root/tensorflow_datasets/cats_vs_dogs/2.0.0...

結果としての tf.data.Dataset オブジェクトは (image, label) ペアを含みます。そこでは画像は可変な shape と 3 チャネルを持ち、ラベルはスカラーです。

print(raw_train)
print(raw_train)
<_OptionsDataset shapes: ((None, None, 3), ()), types: (tf.uint8, tf.int64)>
<_OptionsDataset shapes: ((None, None, 3), ()), types: (tf.uint8, tf.int64)>

訓練セットから最初の 2 つの画像とラベルを表示します。

get_label_name = metadata.features['label'].int2str

for image, label in raw_train.take(2):
  plt.figure()
  plt.imshow(image)
  plt.title(get_label_name(label))

 

データをフォーマットする

タスクのために画像をフォーマットするために tf.image モジュールを使用します。

画像を固定入力サイズにリサイズし、入力チャネルを [-1, 1] の範囲にリスケールします。

IMG_SIZE = 160 # All images will be resized to 160x160

def format_example(image, label):
  image = tf.cast(image, tf.float32)
  image = (image/127.5) - 1
  image = tf.image.resize(image, (IMG_SIZE, IMG_SIZE))
  return image, label

map メソッドを使用してこの関数をデータセットの各アイテムに適用します :

train = raw_train.map(format_example)
validation = raw_validation.map(format_example)
test = raw_test.map(format_example)

データをシャッフルしてバッチ化します。

BATCH_SIZE = 32
SHUFFLE_BUFFER_SIZE = 1000
train_batches = train.shuffle(SHUFFLE_BUFFER_SIZE).batch(BATCH_SIZE)
validation_batches = validation.batch(BATCH_SIZE)
test_batches = test.batch(BATCH_SIZE)

データのバッチを調べます :

for image_batch, label_batch in train_batches.take(1):
  pass

image_batch.shape
TensorShape([32, 160, 160, 3])

 

ベースモデルを作成する

Google で開発されて、ImageNet データセット (web 画像の 1.4 M 画像の巨大データセットと 1000 クラス) で事前訓練された MobileNet V2 からベースモデルを作成します。これはパワフルなモデルです。それが学習した特徴が「猫 vs.犬」問題に何ができるかを見ましょう。

最初に、特徴抽出のために使用する MobileNet V2 の中間層を選択する必要があります。一般的な実践では flatten 演算の前のまさに最後の層の出力を使用します、いわゆる「ボトルネック層」です。ここでの論拠は次の完全結合層はニューラルネットワークがその上で訓練されたタスクに特化され過ぎていることで、これらの層により学習された特徴は新しいタスクのためにあまり有用ではありません。けれども、ボトルネック特徴は遥かに一般性を保持しています。

ImageNet 上で訓練された重みとともに事前ロードされた MobileNet V2 モデルをインスタンス化しましょう。include_top=False 引数を指定することにより、トップに分類層を含まないネットワークをロードします、これは特徴抽出に理想的です。

IMG_SHAPE = (IMG_SIZE, IMG_SIZE, 3)

# Create the base model from the pre-trained model MobileNet V2
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False, 
                                               weights='imagenet')
Downloading data from https://github.com/JonathanCMitchell/mobilenet_v2_keras/releases/download/v1.1/mobilenet_v2_weights_tf_dim_ordering_tf_kernels_1.0_160_no_top.h5
9412608/9406464 [==============================] - 1s 0us/step

この特徴抽出器は各 160x160x3 画像を 5x5x1280 特徴ブロックに変換します。画像のサンプルバッチにそれが何をするかを見ます :

feature_batch = base_model(image_batch)
print(feature_batch.shape)
(32, 5, 5, 1280)

 

特徴抽出

前のステップで作成された畳み込みベースを凍結してそれを特徴抽出器として使用し、その上に分類器を追加して top-level 分類器を訓練します。

 

畳み込みベースを凍結する

compile してモデルを訓練する前に畳み込みベースを凍結することは重要です。凍結する (あるいは layer.trainable = False を設定する) ことにより、この層の重みが訓練の間に更新されることを回避します。

base_model.trainable = False
# Let's take a look at the base model architecture
base_model.summary()
Model: "mobilenetv2_1.00_160"
__________________________________________________________________________________________________
Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
input_1 (InputLayer)            [(None, 160, 160, 3) 0                                            
__________________________________________________________________________________________________
Conv1_pad (ZeroPadding2D)       (None, 161, 161, 3)  0           input_1[0][0]                    
__________________________________________________________________________________________________
Conv1 (Conv2D)                  (None, 80, 80, 32)   864         Conv1_pad[0][0]                  
__________________________________________________________________________________________________
bn_Conv1 (BatchNormalizationV1) (None, 80, 80, 32)   128         Conv1[0][0]                      
__________________________________________________________________________________________________
Conv1_relu (ReLU)               (None, 80, 80, 32)   0           bn_Conv1[0][0]                   
__________________________________________________________________________________________________
expanded_conv_depthwise (Depthw (None, 80, 80, 32)   288         Conv1_relu[0][0]                 
__________________________________________________________________________________________________
expanded_conv_depthwise_BN (Bat (None, 80, 80, 32)   128         expanded_conv_depthwise[0][0]    
__________________________________________________________________________________________________
expanded_conv_depthwise_relu (R (None, 80, 80, 32)   0           expanded_conv_depthwise_BN[0][0] 
__________________________________________________________________________________________________
expanded_conv_project (Conv2D)  (None, 80, 80, 16)   512         expanded_conv_depthwise_relu[0][0
__________________________________________________________________________________________________
expanded_conv_project_BN (Batch (None, 80, 80, 16)   64          expanded_conv_project[0][0]      
__________________________________________________________________________________________________
block_1_expand (Conv2D)         (None, 80, 80, 96)   1536        expanded_conv_project_BN[0][0]   
__________________________________________________________________________________________________
block_1_expand_BN (BatchNormali (None, 80, 80, 96)   384         block_1_expand[0][0]             
__________________________________________________________________________________________________
block_1_expand_relu (ReLU)      (None, 80, 80, 96)   0           block_1_expand_BN[0][0]          
__________________________________________________________________________________________________
block_1_pad (ZeroPadding2D)     (None, 81, 81, 96)   0           block_1_expand_relu[0][0]        
__________________________________________________________________________________________________
block_1_depthwise (DepthwiseCon (None, 40, 40, 96)   864         block_1_pad[0][0]                
__________________________________________________________________________________________________
block_1_depthwise_BN (BatchNorm (None, 40, 40, 96)   384         block_1_depthwise[0][0]          
__________________________________________________________________________________________________
block_1_depthwise_relu (ReLU)   (None, 40, 40, 96)   0           block_1_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_1_project (Conv2D)        (None, 40, 40, 24)   2304        block_1_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_1_project_BN (BatchNormal (None, 40, 40, 24)   96          block_1_project[0][0]            
__________________________________________________________________________________________________
block_2_expand (Conv2D)         (None, 40, 40, 144)  3456        block_1_project_BN[0][0]         
__________________________________________________________________________________________________
block_2_expand_BN (BatchNormali (None, 40, 40, 144)  576         block_2_expand[0][0]             
__________________________________________________________________________________________________
block_2_expand_relu (ReLU)      (None, 40, 40, 144)  0           block_2_expand_BN[0][0]          
__________________________________________________________________________________________________
block_2_depthwise (DepthwiseCon (None, 40, 40, 144)  1296        block_2_expand_relu[0][0]        
__________________________________________________________________________________________________
block_2_depthwise_BN (BatchNorm (None, 40, 40, 144)  576         block_2_depthwise[0][0]          
__________________________________________________________________________________________________
block_2_depthwise_relu (ReLU)   (None, 40, 40, 144)  0           block_2_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_2_project (Conv2D)        (None, 40, 40, 24)   3456        block_2_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_2_project_BN (BatchNormal (None, 40, 40, 24)   96          block_2_project[0][0]            
__________________________________________________________________________________________________
block_2_add (Add)               (None, 40, 40, 24)   0           block_1_project_BN[0][0]         
                                                                 block_2_project_BN[0][0]         
__________________________________________________________________________________________________
block_3_expand (Conv2D)         (None, 40, 40, 144)  3456        block_2_add[0][0]                
__________________________________________________________________________________________________
block_3_expand_BN (BatchNormali (None, 40, 40, 144)  576         block_3_expand[0][0]             
__________________________________________________________________________________________________
block_3_expand_relu (ReLU)      (None, 40, 40, 144)  0           block_3_expand_BN[0][0]          
__________________________________________________________________________________________________
block_3_pad (ZeroPadding2D)     (None, 41, 41, 144)  0           block_3_expand_relu[0][0]        
__________________________________________________________________________________________________
block_3_depthwise (DepthwiseCon (None, 20, 20, 144)  1296        block_3_pad[0][0]                
__________________________________________________________________________________________________
block_3_depthwise_BN (BatchNorm (None, 20, 20, 144)  576         block_3_depthwise[0][0]          
__________________________________________________________________________________________________
block_3_depthwise_relu (ReLU)   (None, 20, 20, 144)  0           block_3_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_3_project (Conv2D)        (None, 20, 20, 32)   4608        block_3_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_3_project_BN (BatchNormal (None, 20, 20, 32)   128         block_3_project[0][0]            
__________________________________________________________________________________________________
block_4_expand (Conv2D)         (None, 20, 20, 192)  6144        block_3_project_BN[0][0]         
__________________________________________________________________________________________________
block_4_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_4_expand[0][0]             
__________________________________________________________________________________________________
block_4_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_4_expand_BN[0][0]          
__________________________________________________________________________________________________
block_4_depthwise (DepthwiseCon (None, 20, 20, 192)  1728        block_4_expand_relu[0][0]        
__________________________________________________________________________________________________
block_4_depthwise_BN (BatchNorm (None, 20, 20, 192)  768         block_4_depthwise[0][0]          
__________________________________________________________________________________________________
block_4_depthwise_relu (ReLU)   (None, 20, 20, 192)  0           block_4_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_4_project (Conv2D)        (None, 20, 20, 32)   6144        block_4_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_4_project_BN (BatchNormal (None, 20, 20, 32)   128         block_4_project[0][0]            
__________________________________________________________________________________________________
block_4_add (Add)               (None, 20, 20, 32)   0           block_3_project_BN[0][0]         
                                                                 block_4_project_BN[0][0]         
__________________________________________________________________________________________________
block_5_expand (Conv2D)         (None, 20, 20, 192)  6144        block_4_add[0][0]                
__________________________________________________________________________________________________
block_5_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_5_expand[0][0]             
__________________________________________________________________________________________________
block_5_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_5_expand_BN[0][0]          
__________________________________________________________________________________________________
block_5_depthwise (DepthwiseCon (None, 20, 20, 192)  1728        block_5_expand_relu[0][0]        
__________________________________________________________________________________________________
block_5_depthwise_BN (BatchNorm (None, 20, 20, 192)  768         block_5_depthwise[0][0]          
__________________________________________________________________________________________________
block_5_depthwise_relu (ReLU)   (None, 20, 20, 192)  0           block_5_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_5_project (Conv2D)        (None, 20, 20, 32)   6144        block_5_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_5_project_BN (BatchNormal (None, 20, 20, 32)   128         block_5_project[0][0]            
__________________________________________________________________________________________________
block_5_add (Add)               (None, 20, 20, 32)   0           block_4_add[0][0]                
                                                                 block_5_project_BN[0][0]         
__________________________________________________________________________________________________
block_6_expand (Conv2D)         (None, 20, 20, 192)  6144        block_5_add[0][0]                
__________________________________________________________________________________________________
block_6_expand_BN (BatchNormali (None, 20, 20, 192)  768         block_6_expand[0][0]             
__________________________________________________________________________________________________
block_6_expand_relu (ReLU)      (None, 20, 20, 192)  0           block_6_expand_BN[0][0]          
__________________________________________________________________________________________________
block_6_pad (ZeroPadding2D)     (None, 21, 21, 192)  0           block_6_expand_relu[0][0]        
__________________________________________________________________________________________________
block_6_depthwise (DepthwiseCon (None, 10, 10, 192)  1728        block_6_pad[0][0]                
__________________________________________________________________________________________________
block_6_depthwise_BN (BatchNorm (None, 10, 10, 192)  768         block_6_depthwise[0][0]          
__________________________________________________________________________________________________
block_6_depthwise_relu (ReLU)   (None, 10, 10, 192)  0           block_6_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_6_project (Conv2D)        (None, 10, 10, 64)   12288       block_6_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_6_project_BN (BatchNormal (None, 10, 10, 64)   256         block_6_project[0][0]            
__________________________________________________________________________________________________
block_7_expand (Conv2D)         (None, 10, 10, 384)  24576       block_6_project_BN[0][0]         
__________________________________________________________________________________________________
block_7_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_7_expand[0][0]             
__________________________________________________________________________________________________
block_7_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_7_expand_BN[0][0]          
__________________________________________________________________________________________________
block_7_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_7_expand_relu[0][0]        
__________________________________________________________________________________________________
block_7_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_7_depthwise[0][0]          
__________________________________________________________________________________________________
block_7_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_7_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_7_project (Conv2D)        (None, 10, 10, 64)   24576       block_7_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_7_project_BN (BatchNormal (None, 10, 10, 64)   256         block_7_project[0][0]            
__________________________________________________________________________________________________
block_7_add (Add)               (None, 10, 10, 64)   0           block_6_project_BN[0][0]         
                                                                 block_7_project_BN[0][0]         
__________________________________________________________________________________________________
block_8_expand (Conv2D)         (None, 10, 10, 384)  24576       block_7_add[0][0]                
__________________________________________________________________________________________________
block_8_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_8_expand[0][0]             
__________________________________________________________________________________________________
block_8_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_8_expand_BN[0][0]          
__________________________________________________________________________________________________
block_8_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_8_expand_relu[0][0]        
__________________________________________________________________________________________________
block_8_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_8_depthwise[0][0]          
__________________________________________________________________________________________________
block_8_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_8_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_8_project (Conv2D)        (None, 10, 10, 64)   24576       block_8_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_8_project_BN (BatchNormal (None, 10, 10, 64)   256         block_8_project[0][0]            
__________________________________________________________________________________________________
block_8_add (Add)               (None, 10, 10, 64)   0           block_7_add[0][0]                
                                                                 block_8_project_BN[0][0]         
__________________________________________________________________________________________________
block_9_expand (Conv2D)         (None, 10, 10, 384)  24576       block_8_add[0][0]                
__________________________________________________________________________________________________
block_9_expand_BN (BatchNormali (None, 10, 10, 384)  1536        block_9_expand[0][0]             
__________________________________________________________________________________________________
block_9_expand_relu (ReLU)      (None, 10, 10, 384)  0           block_9_expand_BN[0][0]          
__________________________________________________________________________________________________
block_9_depthwise (DepthwiseCon (None, 10, 10, 384)  3456        block_9_expand_relu[0][0]        
__________________________________________________________________________________________________
block_9_depthwise_BN (BatchNorm (None, 10, 10, 384)  1536        block_9_depthwise[0][0]          
__________________________________________________________________________________________________
block_9_depthwise_relu (ReLU)   (None, 10, 10, 384)  0           block_9_depthwise_BN[0][0]       
__________________________________________________________________________________________________
block_9_project (Conv2D)        (None, 10, 10, 64)   24576       block_9_depthwise_relu[0][0]     
__________________________________________________________________________________________________
block_9_project_BN (BatchNormal (None, 10, 10, 64)   256         block_9_project[0][0]            
__________________________________________________________________________________________________
block_9_add (Add)               (None, 10, 10, 64)   0           block_8_add[0][0]                
                                                                 block_9_project_BN[0][0]         
__________________________________________________________________________________________________
block_10_expand (Conv2D)        (None, 10, 10, 384)  24576       block_9_add[0][0]                
__________________________________________________________________________________________________
block_10_expand_BN (BatchNormal (None, 10, 10, 384)  1536        block_10_expand[0][0]            
__________________________________________________________________________________________________
block_10_expand_relu (ReLU)     (None, 10, 10, 384)  0           block_10_expand_BN[0][0]         
__________________________________________________________________________________________________
block_10_depthwise (DepthwiseCo (None, 10, 10, 384)  3456        block_10_expand_relu[0][0]       
__________________________________________________________________________________________________
block_10_depthwise_BN (BatchNor (None, 10, 10, 384)  1536        block_10_depthwise[0][0]         
__________________________________________________________________________________________________
block_10_depthwise_relu (ReLU)  (None, 10, 10, 384)  0           block_10_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_10_project (Conv2D)       (None, 10, 10, 96)   36864       block_10_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_10_project_BN (BatchNorma (None, 10, 10, 96)   384         block_10_project[0][0]           
__________________________________________________________________________________________________
block_11_expand (Conv2D)        (None, 10, 10, 576)  55296       block_10_project_BN[0][0]        
__________________________________________________________________________________________________
block_11_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_11_expand[0][0]            
__________________________________________________________________________________________________
block_11_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_11_expand_BN[0][0]         
__________________________________________________________________________________________________
block_11_depthwise (DepthwiseCo (None, 10, 10, 576)  5184        block_11_expand_relu[0][0]       
__________________________________________________________________________________________________
block_11_depthwise_BN (BatchNor (None, 10, 10, 576)  2304        block_11_depthwise[0][0]         
__________________________________________________________________________________________________
block_11_depthwise_relu (ReLU)  (None, 10, 10, 576)  0           block_11_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_11_project (Conv2D)       (None, 10, 10, 96)   55296       block_11_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_11_project_BN (BatchNorma (None, 10, 10, 96)   384         block_11_project[0][0]           
__________________________________________________________________________________________________
block_11_add (Add)              (None, 10, 10, 96)   0           block_10_project_BN[0][0]        
                                                                 block_11_project_BN[0][0]        
__________________________________________________________________________________________________
block_12_expand (Conv2D)        (None, 10, 10, 576)  55296       block_11_add[0][0]               
__________________________________________________________________________________________________
block_12_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_12_expand[0][0]            
__________________________________________________________________________________________________
block_12_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_12_expand_BN[0][0]         
__________________________________________________________________________________________________
block_12_depthwise (DepthwiseCo (None, 10, 10, 576)  5184        block_12_expand_relu[0][0]       
__________________________________________________________________________________________________
block_12_depthwise_BN (BatchNor (None, 10, 10, 576)  2304        block_12_depthwise[0][0]         
__________________________________________________________________________________________________
block_12_depthwise_relu (ReLU)  (None, 10, 10, 576)  0           block_12_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_12_project (Conv2D)       (None, 10, 10, 96)   55296       block_12_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_12_project_BN (BatchNorma (None, 10, 10, 96)   384         block_12_project[0][0]           
__________________________________________________________________________________________________
block_12_add (Add)              (None, 10, 10, 96)   0           block_11_add[0][0]               
                                                                 block_12_project_BN[0][0]        
__________________________________________________________________________________________________
block_13_expand (Conv2D)        (None, 10, 10, 576)  55296       block_12_add[0][0]               
__________________________________________________________________________________________________
block_13_expand_BN (BatchNormal (None, 10, 10, 576)  2304        block_13_expand[0][0]            
__________________________________________________________________________________________________
block_13_expand_relu (ReLU)     (None, 10, 10, 576)  0           block_13_expand_BN[0][0]         
__________________________________________________________________________________________________
block_13_pad (ZeroPadding2D)    (None, 11, 11, 576)  0           block_13_expand_relu[0][0]       
__________________________________________________________________________________________________
block_13_depthwise (DepthwiseCo (None, 5, 5, 576)    5184        block_13_pad[0][0]               
__________________________________________________________________________________________________
block_13_depthwise_BN (BatchNor (None, 5, 5, 576)    2304        block_13_depthwise[0][0]         
__________________________________________________________________________________________________
block_13_depthwise_relu (ReLU)  (None, 5, 5, 576)    0           block_13_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_13_project (Conv2D)       (None, 5, 5, 160)    92160       block_13_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_13_project_BN (BatchNorma (None, 5, 5, 160)    640         block_13_project[0][0]           
__________________________________________________________________________________________________
block_14_expand (Conv2D)        (None, 5, 5, 960)    153600      block_13_project_BN[0][0]        
__________________________________________________________________________________________________
block_14_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_14_expand[0][0]            
__________________________________________________________________________________________________
block_14_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_14_expand_BN[0][0]         
__________________________________________________________________________________________________
block_14_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_14_expand_relu[0][0]       
__________________________________________________________________________________________________
block_14_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_14_depthwise[0][0]         
__________________________________________________________________________________________________
block_14_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_14_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_14_project (Conv2D)       (None, 5, 5, 160)    153600      block_14_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_14_project_BN (BatchNorma (None, 5, 5, 160)    640         block_14_project[0][0]           
__________________________________________________________________________________________________
block_14_add (Add)              (None, 5, 5, 160)    0           block_13_project_BN[0][0]        
                                                                 block_14_project_BN[0][0]        
__________________________________________________________________________________________________
block_15_expand (Conv2D)        (None, 5, 5, 960)    153600      block_14_add[0][0]               
__________________________________________________________________________________________________
block_15_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_15_expand[0][0]            
__________________________________________________________________________________________________
block_15_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_15_expand_BN[0][0]         
__________________________________________________________________________________________________
block_15_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_15_expand_relu[0][0]       
__________________________________________________________________________________________________
block_15_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_15_depthwise[0][0]         
__________________________________________________________________________________________________
block_15_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_15_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_15_project (Conv2D)       (None, 5, 5, 160)    153600      block_15_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_15_project_BN (BatchNorma (None, 5, 5, 160)    640         block_15_project[0][0]           
__________________________________________________________________________________________________
block_15_add (Add)              (None, 5, 5, 160)    0           block_14_add[0][0]               
                                                                 block_15_project_BN[0][0]        
__________________________________________________________________________________________________
block_16_expand (Conv2D)        (None, 5, 5, 960)    153600      block_15_add[0][0]               
__________________________________________________________________________________________________
block_16_expand_BN (BatchNormal (None, 5, 5, 960)    3840        block_16_expand[0][0]            
__________________________________________________________________________________________________
block_16_expand_relu (ReLU)     (None, 5, 5, 960)    0           block_16_expand_BN[0][0]         
__________________________________________________________________________________________________
block_16_depthwise (DepthwiseCo (None, 5, 5, 960)    8640        block_16_expand_relu[0][0]       
__________________________________________________________________________________________________
block_16_depthwise_BN (BatchNor (None, 5, 5, 960)    3840        block_16_depthwise[0][0]         
__________________________________________________________________________________________________
block_16_depthwise_relu (ReLU)  (None, 5, 5, 960)    0           block_16_depthwise_BN[0][0]      
__________________________________________________________________________________________________
block_16_project (Conv2D)       (None, 5, 5, 320)    307200      block_16_depthwise_relu[0][0]    
__________________________________________________________________________________________________
block_16_project_BN (BatchNorma (None, 5, 5, 320)    1280        block_16_project[0][0]           
__________________________________________________________________________________________________
Conv_1 (Conv2D)                 (None, 5, 5, 1280)   409600      block_16_project_BN[0][0]        
__________________________________________________________________________________________________
Conv_1_bn (BatchNormalizationV1 (None, 5, 5, 1280)   5120        Conv_1[0][0]                     
__________________________________________________________________________________________________
out_relu (ReLU)                 (None, 5, 5, 1280)   0           Conv_1_bn[0][0]                  
==================================================================================================
Total params: 2,257,984
Trainable params: 0
Non-trainable params: 2,257,984
__________________________________________________________________________________________________

 

分類ヘッドを追加する

特徴ブロックから予測を生成するために、特徴を画像毎に単一 1280-要素ベクトルに変換するために tf.keras.layers.GlobalAveragePlloing2d を使用して 5×5 空間的位置に渡り平均します。

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
print(feature_batch_average.shape)
(32, 1280)

その上にこれらの特徴を画像毎に単一の予測に変換するために tf.keras.layers.Dense 層を適用します。ここでは活性化関数は使用しないでください、何故ならばこの予測はロジットとして扱われるからです : 正の数はクラス 1 を予測し、負の数はクラス 0 を予測します。

prediction_layer = keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)
(32, 1)

さて特徴抽出器これらの 2 つの層を tf.keras.Sequential モデルを使用してスタックします :

model = tf.keras.Sequential([
  base_model,
  global_average_layer,
  prediction_layer
])

 

モデルをコンパイルする

モデルを訓練する前にそれをコンパイルしなければなりません。

base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=base_learning_rate), 
              loss='binary_crossentropy', 
              metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_160 (Model) (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,281
Non-trainable params: 2,257,984
_________________________________________________________________

これら 1.2K の訓練可能なパラメータは 2 つの tf.Variable オブジェクトの中で分割されます、2 つの dense 層の重みとバイアスです :

len(model.trainable_variables)
2

 

モデルを訓練する

10 エポックの間の訓練後、 ~96% 精度を得ることができます。

num_train, num_val, num_test = (
  metadata.splits['train'].num_examples*weight/10
  for weight in SPLIT_WEIGHTS
)
initial_epochs = 10
steps_per_epoch = round(num_train)//BATCH_SIZE
validation_steps = 20

loss0,accuracy0 = model.evaluate(validation_batches, steps = validation_steps)
20/20 [==============================] - 4s 219ms/step - loss: 3.1885 - accuracy: 0.6109
print("initial loss: {:.2f}".format(loss0))
print("initial accuracy: {:.2f}".format(accuracy0))
initial loss: 3.19
initial accuracy: 0.61
history = model.fit(train_batches.repeat(),
                    epochs=initial_epochs,
                    steps_per_epoch = steps_per_epoch,
                    validation_data=validation_batches.repeat(), 
                    validation_steps=validation_steps)
Epoch 1/10
581/581 [==============================] - 102s 175ms/step - loss: 1.8917 - accuracy: 0.7606 - val_loss: 0.8860 - val_accuracy: 0.8828
Epoch 2/10
581/581 [==============================] - 94s 161ms/step - loss: 0.9989 - accuracy: 0.8697 - val_loss: 0.4330 - val_accuracy: 0.9281
Epoch 3/10
581/581 [==============================] - 99s 170ms/step - loss: 0.7417 - accuracy: 0.8995 - val_loss: 0.2629 - val_accuracy: 0.9469
Epoch 4/10
581/581 [==============================] - 94s 162ms/step - loss: 0.6681 - accuracy: 0.9133 - val_loss: 0.2753 - val_accuracy: 0.9563
Epoch 5/10
581/581 [==============================] - 98s 169ms/step - loss: 0.5944 - accuracy: 0.9220 - val_loss: 0.2410 - val_accuracy: 0.9641
Epoch 6/10
581/581 [==============================] - 98s 169ms/step - loss: 0.5779 - accuracy: 0.9252 - val_loss: 0.2003 - val_accuracy: 0.9656
Epoch 7/10
581/581 [==============================] - 94s 162ms/step - loss: 0.5151 - accuracy: 0.9328 - val_loss: 0.2115 - val_accuracy: 0.9688
Epoch 8/10
581/581 [==============================] - 95s 164ms/step - loss: 0.5076 - accuracy: 0.9346 - val_loss: 0.1678 - val_accuracy: 0.9703
Epoch 9/10
581/581 [==============================] - 96s 165ms/step - loss: 0.4679 - accuracy: 0.9368 - val_loss: 0.1668 - val_accuracy: 0.9703
Epoch 10/10
581/581 [==============================] - 96s 165ms/step - loss: 0.4921 - accuracy: 0.9381 - val_loss: 0.1847 - val_accuracy: 0.9719

 

学習カーブ

MobileNet V2 ベースモデルを固定特徴抽出器として使用するときの、訓練と検証精度 / 損失の学習カーブを見てみましょう。

acc = history.history['accuracy']
val_acc = history.history['val_accuracy']

loss = history.history['loss']
val_loss = history.history['val_loss']

plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.ylabel('Accuracy')
plt.ylim([min(plt.ylim()),1])
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.ylabel('Cross Entropy')
plt.ylim([0,1.0])
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

Note: もし貴方が何故検証メトリクスが訓練メトリクスよりも明らかに良いのか不思議に思うのであれば、主要因は訓練をより困難にするランダムネスを追加する tf.keras.layers.BatchNormalization と tf.keras.layers.Dropout のような層のためです。程度は少ないですが、それはまた検証メトリクスがエポック後に評価される一方で訓練メトリクスがエポックのための平均を報告するからです、そのため検証メトリクスは僅かばかり長く訓練されたモデルを見ます。

 

再調整

私達の特徴抽出実験では、MobileNet V2 ベースモデルの上の 2, 3 層だけを訓練していました。事前訓練されたネットワークの重みは訓練の間に更新されませんでした。より以上にパフォーマンスを増す一つの方法は top-level 分類器の訓練と一緒に事前訓練されたモデルの上部の層の重みを「再調整」することです。訓練過程は一般的な特徴マップから私達のデータセットに特に関連する特徴へと重みが調整されることを強制します。

Note: これは事前訓練されたモデルを非訓練可能に設定しながら top-level 分類器を訓練した後で試されるべきです。もし貴方が事前訓練されたモデルの上のランダムに初期化された分類器を追加して総ての層を一緒に訓練することを試みる場合、勾配更新の大きさが (分類器からのランダム重みゆえに) 大きすぎて貴方の事前訓練されたモデルはそれが学習したことを総て単に忘れるでしょう。

更に、事前訓練されたモデルの総ての層ではなく事前訓練されたモデルの top 層を再調整する裏の根拠は以下です : convnet では、層が高位になればなるほど、それはより特化されます。convnet の最初の 2, 3 の層は非常に単純で一般的な特徴を学習して、それは殆ど総てのタイプの画像に一般化されます。しかしより高く行くほどに、特徴は次第に (モデルがその上で訓練された) データセット特有になります。再調整の目標はこれらの専門的な特徴を新しいデータで動作するように適応させることです。

 

モデルのトップ層を解凍する

私達が行なう必要がある総てのことは base_model を解凍することです、そしてボトム層は非訓練可能に設定します。それから、モデルを再コンパイルして (これらの変更が効果を持つようにするために必要です)、訓練を再開します。

base_model.trainable = True
# Let's take a look to see how many layers are in the base model
print("Number of layers in the base model: ", len(base_model.layers))

# Fine tune from this layer onwards
fine_tune_at = 100

# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
  layer.trainable =  False
Number of layers in the base model:  155

 

モデルをコンパイルする

遥かに低い訓練率 (= training rate) を使用してモデルをコンパイルします。

model.compile(loss='binary_crossentropy',
              optimizer = tf.keras.optimizers.RMSprop(lr=base_learning_rate/10),
              metrics=['accuracy'])
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
mobilenetv2_1.00_160 (Model) (None, 5, 5, 1280)        2257984   
_________________________________________________________________
global_average_pooling2d (Gl (None, 1280)              0         
_________________________________________________________________
dense (Dense)                (None, 1)                 1281      
=================================================================
Total params: 2,259,265
Trainable params: 1,863,873
Non-trainable params: 395,392
_________________________________________________________________
len(model.trainable_variables)
58

 

モデルの訓練を継続する

先に収束するために訓練したのであれば、これは数パーセントの更なる精度を貴方に得させるでしょう。

fine_tune_epochs = 10
total_epochs =  initial_epochs + fine_tune_epochs

history_fine = model.fit(train_batches.repeat(), 
                         steps_per_epoch = steps_per_epoch,
                         epochs=total_epochs, 
                         initial_epoch = initial_epochs,
                         validation_data=validation_batches.repeat(), 
                         validation_steps=validation_steps)
Epoch 11/20
581/581 [==============================] - 134s 231ms/step - loss: 0.4208 - accuracy: 0.9484 - val_loss: 0.1907 - val_accuracy: 0.9812
Epoch 12/20
581/581 [==============================] - 114s 197ms/step - loss: 0.3359 - accuracy: 0.9570 - val_loss: 0.1835 - val_accuracy: 0.9844
Epoch 13/20
581/581 [==============================] - 116s 200ms/step - loss: 0.2930 - accuracy: 0.9650 - val_loss: 0.1505 - val_accuracy: 0.9844
Epoch 14/20
581/581 [==============================] - 114s 196ms/step - loss: 0.2561 - accuracy: 0.9701 - val_loss: 0.1575 - val_accuracy: 0.9859
Epoch 15/20
581/581 [==============================] - 119s 206ms/step - loss: 0.2302 - accuracy: 0.9715 - val_loss: 0.1600 - val_accuracy: 0.9812
Epoch 16/20
581/581 [==============================] - 115s 197ms/step - loss: 0.2134 - accuracy: 0.9747 - val_loss: 0.1407 - val_accuracy: 0.9828
Epoch 17/20
581/581 [==============================] - 115s 197ms/step - loss: 0.1546 - accuracy: 0.9813 - val_loss: 0.0944 - val_accuracy: 0.9828
Epoch 18/20
581/581 [==============================] - 116s 200ms/step - loss: 0.1636 - accuracy: 0.9794 - val_loss: 0.0947 - val_accuracy: 0.9844
Epoch 19/20
581/581 [==============================] - 115s 198ms/step - loss: 0.1356 - accuracy: 0.9823 - val_loss: 0.1169 - val_accuracy: 0.9828
Epoch 20/20
581/581 [==============================] - 116s 199ms/step - loss: 0.1243 - accuracy: 0.9849 - val_loss: 0.1121 - val_accuracy: 0.9875

 

学習カーブ

再調整後、訓練と検証精度 / 損失の学習カーブを見てみましょう。

Note: 訓練データセットは非常に小さくて MobileNet V2 がその上で訓練された元のデータセットに類似していますので、再調整は overfitting の結果になるかもしれません。

再調整後にモデルは 98% 精度近くに到達します。

acc += history_fine.history['accuracy']
val_acc += history_fine.history['val_accuracy']

loss += history_fine.history['loss']
val_loss += history_fine.history['val_loss']
plt.figure(figsize=(8, 8))
plt.subplot(2, 1, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.ylim([0.8, 1])
plt.plot([initial_epochs-1,initial_epochs-1], 
          plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(2, 1, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.ylim([0, 1.0])
plt.plot([initial_epochs-1,initial_epochs-1], 
         plt.ylim(), label='Start Fine Tuning')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.xlabel('epoch')
plt.show()

 

Key takeaways

要約すれば、精度を改善するために事前訓練されたモデルを使用してどのように転移学習を行なうかについてこのチュートリアルでカバーしたものがここにあります : * 特徴抽出 のために事前訓練されたモデルを使用する – 小さいデータセットで作業するとき、同じドメインのより巨大なデータセット上で訓練されたモデルにより学習された特徴を活用することは一般的です。これは事前訓練されたモデルをインスタンス化して完全結合分類器を上に追加することにより成されます。事前訓練されたモデルは「凍結」されて訓練の間分類器の重みだけが更新されます。この場合、畳み込みベースが各画像に関係する総ての特徴を抽出してそして特徴のこれらのセットが与えられたときそれがどのクラスに属するか決定する分類器を訓練します。
* 事前訓練されたモデルを 再調整 する – パフォーマンスを更に改良するために、事前訓練されたモデルの top-level 層を再調整を通して新しいデータセットに再目的化することを望むかもしれません。この場合、データセットに固有の極度に特化されて高位な特徴を学習するように重みを調整します。これは、訓練データセットが巨大で (事前訓練されたモデルがその上で訓練された) 元のデータセットに非常に類似しているときに限り意味を持ちます。

 

以上



TensorFlow 2.0 Alpha : Tutorials : 画像 :- 畳み込みニューラルネットワーク

TensorFlow 2.0 Alpha : Beginner Tutorials : 画像 :- 畳み込みニューラルネットワーク (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 04/01/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – Images の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

画像 :- 畳み込みニューラルネットワーク

このチュートリアルは MNIST 数字を分類するための単純な 畳み込みニューラルネットワーク (CNN) の訓練を実演します。この単純なネットワークは MNIST テストセット上で 99% 超の精度を獲得します。このチュートリアルは Keras Sequential API を使用しますので、モデルの作成と訓練は単にコードの数行を取るだけです。

 

Import TensorFlow

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

from tensorflow.keras import datasets, layers, models

 

MNIST データセットをダウンロードして準備する

(train_images, train_labels), (test_images, test_labels) = datasets.mnist.load_data()

train_images = train_images.reshape((60000, 28, 28, 1))
test_images = test_images.reshape((10000, 28, 28, 1))

# Normalize pixel values to be between 0 and 1
train_images, test_images = train_images / 255.0, test_images / 255.0
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

 

畳み込みベースを作成する

下の 6 行のコードは一般的なパターン: Conv2DMaxPooling2D 層のスタックを使用して畳み込みベースを作成します。

入力として、CNN は shape (image_height, image_width, color_channels) の tensor を取り、バッチサイズは無視します。カラーチャネルに馴染みがないのであれば、カラー画像が 3 つ (R, G, B) を持つところを MNIST は一つ持ちます (何故ならば画像がグレースケールだからです)。この例では、MNIST 画像のフォーマットである shape (28, 28, 1) の入力を処理する CNN を構成します。
引数 input_shape を最初の層に渡すことによりこれを成します。

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

ここまでのモデルの構造を表示しましょう。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
=================================================================
Total params: 55,744
Trainable params: 55,744
Non-trainable params: 0
_________________________________________________________________

上で、総ての Conv2D と MaxPooling2D 層の出力が 3D tensor の shape (height, width, channels) であることを見ることができます。幅と高さの次元はネットワークで深くなるにつれて縮小する傾向にあります。各 Conv2D の出力チャネル数は最初の引数 (e.g., 32 or 64) により制御されます。典型的には、幅と高さが縮小するにつれて、各 Conv2D 層でより多くの出力チャネルを追加する能力が (計算的に) あります。

 

上に Dense 層を追加する

モデルを完成するために、分類を遂行するために (shape (3, 3, 64) の) 畳み込みベースからの最後の出力 tensor を一つかそれ以上の Dense 層に供給します。Dense 層は入力としてベクトル (それは 1D です) を取りますが、その一方で現在の出力は 3D tensor です。最初に、3D 出力を 1D に平坦化 (or 展開) して、それから一つかそれ以上の Dense 層を上に追加します。MNIST は 10 出力クラスを持ちますので、10 出力と softmax 活性を持つ最後の Dense 層を使用します。

model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

ここにモデルの完全なアーキテクチャがあります。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 26, 26, 32)        320       
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 13, 13, 32)        0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 11, 11, 64)        18496     
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 5, 5, 64)          0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 3, 3, 64)          36928     
_________________________________________________________________
flatten (Flatten)            (None, 576)               0         
_________________________________________________________________
dense (Dense)                (None, 64)                36928     
_________________________________________________________________
dense_1 (Dense)              (None, 10)                650       
=================================================================
Total params: 93,322
Trainable params: 93,322
Non-trainable params: 0
_________________________________________________________________

見れるように、2 つの Dense 層を通る前に (3, 3, 64) 出力は shape (576) のベクトルに平坦化されます。

 

モデルをコンパイルして訓練する

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

model.fit(train_images, train_labels, epochs=5)
Epoch 1/5
60000/60000 [==============================] - 16s 271us/sample - loss: 0.1495 - accuracy: 0.9536
Epoch 2/5
60000/60000 [==============================] - 16s 267us/sample - loss: 0.0459 - accuracy: 0.9855
Epoch 3/5
60000/60000 [==============================] - 16s 266us/sample - loss: 0.0326 - accuracy: 0.9897
Epoch 4/5
60000/60000 [==============================] - 16s 266us/sample - loss: 0.0259 - accuracy: 0.9917
Epoch 5/5
60000/60000 [==============================] - 16s 266us/sample - loss: 0.0203 - accuracy: 0.9938

<tensorflow.python.keras.callbacks.History at 0x7f048fcdb630>

 

モデルを評価する

test_loss, test_acc = model.evaluate(test_images, test_labels)
10000/10000 [==============================] - 1s 100us/sample - loss: 0.0307 - accuracy: 0.9908
print(test_acc)
0.9908
 

以上



TensorFlow 2.0 Alpha : Tutorials : ML 基本 :- モデルをセーブしてリストアする

TensorFlow 2.0 Alpha : Beginner Tutorials : ML 基本 :- モデルをセーブしてリストアする (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/31/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- モデルをセーブしてリストアする

モデルの進捗は訓練の間に — そして後に — セーブすることが可能です。これは、モデルが (訓練を) 中止したところから再開できて長い訓練時間を回避できることを意味します。セービングはまた貴方がモデルを共有して他の人は貴方のワークを再作成できることもまた意味します。研究モデルとテクニックを公開するとき、多くの機械学習実践者は以下を共有します :

  • モデルを作成するためのコード、そして
  • モデルのために訓練された重み、またはパラメータ

このデータの共有は他の人たちがモデルがどのように動作するかを理解して新しいデータで彼ら自身がそれを試す手助けとなります。

Caution: 信頼できない (untrusted) コードに注意してください — TensorFlow モデルはコードです。詳細は Using TensorFlow Securely を見てください。

 

オプション

TensorFlow モデルをセーブする異なる方法があります — 貴方が使用している API に依拠します。このガイドは tf.keras, TensorFlow でモデルを構築して訓練するための高位 API を使用します。他のアプローチについては、TensorFlow Save and Restore ガイドや Saving in eager を見てください。

 

セットアップ

インストールとインポート

TensorFlow と依存関係をインストールしてインポートします :

!pip install -q h5py pyyaml 

 

サンプル・データセットを取得する

重みのセーブを実演するために MNIST データセット を使用してモデルを訓練します。これらのデモの実行をスピードアップするために、最初の 1000 サンプルだけを使用します :

from __future__ import absolute_import, division, print_function

import os

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
from tensorflow import keras

tf.__version__
'2.0.0-alpha0'
(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()

train_labels = train_labels[:1000]
test_labels = test_labels[:1000]

train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0
test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

 

モデルを定義する

重みのセーブとロードを実演するために使用する単純なモデルを構築しましょう。

# Returns a short sequential model
def create_model():
  model = tf.keras.models.Sequential([
    keras.layers.Dense(512, activation='relu', input_shape=(784,)),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(10, activation='softmax')
  ])
  
  model.compile(optimizer='adam', 
                loss='sparse_categorical_crossentropy',
                metrics=['accuracy'])
  
  return model


# Create a basic model instance
model = create_model()
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 512)               401920    
_________________________________________________________________
dropout (Dropout)            (None, 512)               0         
_________________________________________________________________
dense_1 (Dense)              (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

 

訓練の間にチェックポイントをセーブする

主要なユースケースは訓練の間と最後にチェックポイントを自動的にセーブすることです。このように訓練されたモデルをそれを再訓練しなければならないことなく使用できたり、あるいは — 訓練プロセスが中断された場合には — 中止したところから訓練を選択することができます。

tf.keras.callbacks.ModelCheckpoint はこのタスクを遂行する callback です。この callback はチェックポイントを構成するために 2, 3 の引数を取ります。

 

Checkpoint callback 使用方法

モデルを訓練してそれを ModelCheckpoint callback に渡します :

checkpoint_path = "training_1/cp.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

# Create checkpoint callback
cp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path, 
                                                 save_weights_only=True,
                                                 verbose=1)

model = create_model()

model.fit(train_images, train_labels,  epochs = 10, 
          validation_data = (test_images,test_labels),
          callbacks = [cp_callback])  # pass callback to training

# This may generate warnings related to saving the state of the optimizer.
# These warnings (and similar warnings throughout this notebook)
# are in place to discourage outdated usage, and can be ignored.
Train on 1000 samples, validate on 1000 samples
Epoch 1/10
 576/1000 [================>.............] - ETA: 0s - loss: 1.5005 - accuracy: 0.5486
Epoch 00001: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 386us/sample - loss: 1.1582 - accuracy: 0.6670 - val_loss: 0.6874 - val_accuracy: 0.7960
Epoch 2/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.4371 - accuracy: 0.8676
Epoch 00002: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 169us/sample - loss: 0.4171 - accuracy: 0.8700 - val_loss: 0.5031 - val_accuracy: 0.8480
Epoch 3/10
 544/1000 [===============>..............] - ETA: 0s - loss: 0.3069 - accuracy: 0.9228
Epoch 00003: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 184us/sample - loss: 0.2854 - accuracy: 0.9260 - val_loss: 0.4601 - val_accuracy: 0.8610
Epoch 4/10
 960/1000 [===========================>..] - ETA: 0s - loss: 0.2190 - accuracy: 0.9375
Epoch 00004: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 186us/sample - loss: 0.2174 - accuracy: 0.9380 - val_loss: 0.4377 - val_accuracy: 0.8670
Epoch 5/10
 960/1000 [===========================>..] - ETA: 0s - loss: 0.1558 - accuracy: 0.9688
Epoch 00005: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 409us/sample - loss: 0.1523 - accuracy: 0.9700 - val_loss: 0.4070 - val_accuracy: 0.8600
Epoch 6/10
 960/1000 [===========================>..] - ETA: 0s - loss: 0.1163 - accuracy: 0.9750
Epoch 00006: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 203us/sample - loss: 0.1152 - accuracy: 0.9760 - val_loss: 0.4117 - val_accuracy: 0.8600
Epoch 7/10
 960/1000 [===========================>..] - ETA: 0s - loss: 0.0932 - accuracy: 0.9854
Epoch 00007: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 203us/sample - loss: 0.0930 - accuracy: 0.9850 - val_loss: 0.4309 - val_accuracy: 0.8620
Epoch 8/10
 960/1000 [===========================>..] - ETA: 0s - loss: 0.0697 - accuracy: 0.9937
Epoch 00008: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 197us/sample - loss: 0.0681 - accuracy: 0.9940 - val_loss: 0.4407 - val_accuracy: 0.8610
Epoch 9/10
 928/1000 [==========================>...] - ETA: 0s - loss: 0.0523 - accuracy: 0.9978
Epoch 00009: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 207us/sample - loss: 0.0507 - accuracy: 0.9980 - val_loss: 0.3949 - val_accuracy: 0.8650
Epoch 10/10
 928/1000 [==========================>...] - ETA: 0s - loss: 0.0357 - accuracy: 0.9989
Epoch 00010: saving model to training_1/cp.ckpt
1000/1000 [==============================] - 0s 207us/sample - loss: 0.0368 - accuracy: 0.9990 - val_loss: 0.4168 - val_accuracy: 0.8660

<tensorflow.python.keras.callbacks.History at 0x7fb0c5d080f0>

これは、各エポックの最後に更新される TensorFlow チェックポイント・ファイルの単一のコレクションを作成します :

!ls {checkpoint_dir}
checkpoint  cp.ckpt.data-00000-of-00001  cp.ckpt.index

新しい、未訓練のモデルを作成しましょう。重みだけからモデルをリストアするときは、元のモデルと同じアーキテクチャを持つモデルを持たなければなりません。それは同じモデル・アーキテクチャですから、それがモデルの異なるインスタンスであるにもかかわらず重みを共有することができます。

さて未使用の、未訓練のモデルを再構築して、それをテストセット上で評価しましょう。未訓練モデルは偶然レベル (~10% 精度) で遂行します :

model = create_model()

loss, acc = model.evaluate(test_images, test_labels)
print("Untrained model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 100us/sample - loss: 2.3379 - accuracy: 0.1330
Untrained model, accuracy: 13.30%

それからチェックポイントから重みをロードして、再評価します :

model.load_weights(checkpoint_path)
loss,acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 48us/sample - loss: 0.4168 - accuracy: 0.8660
Restored model, accuracy: 86.60%

 

Checkpoint callback オプション

callback は結果としてのチェックポイントに一意の名前を与えて、チェックポイントする頻度を調整するために幾つかのオプションを提供します。

新しいモデルを訓練して、一意に名前付けられたチェックポイントを 5 エポック毎に一度セーブします :

# include the epoch in the file name. (uses `str.format`)
checkpoint_path = "training_2/cp-{epoch:04d}.ckpt"
checkpoint_dir = os.path.dirname(checkpoint_path)

cp_callback = tf.keras.callbacks.ModelCheckpoint(
    checkpoint_path, verbose=1, save_weights_only=True,
    # Save weights, every 5-epochs.
    period=5)

model = create_model()
model.save_weights(checkpoint_path.format(epoch=0))
model.fit(train_images, train_labels,
          epochs = 50, callbacks = [cp_callback],
          validation_data = (test_images,test_labels),
          verbose=0)

Epoch 00005: saving model to training_2/cp-0005.ckpt

Epoch 00010: saving model to training_2/cp-0010.ckpt

Epoch 00015: saving model to training_2/cp-0015.ckpt

Epoch 00020: saving model to training_2/cp-0020.ckpt

Epoch 00025: saving model to training_2/cp-0025.ckpt

Epoch 00030: saving model to training_2/cp-0030.ckpt

Epoch 00035: saving model to training_2/cp-0035.ckpt

Epoch 00040: saving model to training_2/cp-0040.ckpt

Epoch 00045: saving model to training_2/cp-0045.ckpt

Epoch 00050: saving model to training_2/cp-0050.ckpt

<tensorflow.python.keras.callbacks.History at 0x7fb0d04bcc18>

さて、結果としてのチェックポイントを見て最新の一つを選択します :

! ls {checkpoint_dir}
checkpoint            cp-0025.ckpt.index
cp-0000.ckpt.data-00000-of-00001  cp-0030.ckpt.data-00000-of-00001
cp-0000.ckpt.index        cp-0030.ckpt.index
cp-0005.ckpt.data-00000-of-00001  cp-0035.ckpt.data-00000-of-00001
cp-0005.ckpt.index        cp-0035.ckpt.index
cp-0010.ckpt.data-00000-of-00001  cp-0040.ckpt.data-00000-of-00001
cp-0010.ckpt.index        cp-0040.ckpt.index
cp-0015.ckpt.data-00000-of-00001  cp-0045.ckpt.data-00000-of-00001
cp-0015.ckpt.index        cp-0045.ckpt.index
cp-0020.ckpt.data-00000-of-00001  cp-0050.ckpt.data-00000-of-00001
cp-0020.ckpt.index        cp-0050.ckpt.index
cp-0025.ckpt.data-00000-of-00001
latest = tf.train.latest_checkpoint(checkpoint_dir)
latest
'training_2/cp-0050.ckpt'

Note: デフォルトの tensorflow フォーマットは 5 つの直近のチェックポイントだけをセーブします。

テストするためには、モデルをリセットして最新のチェックポイントをロードします :

model = create_model()
model.load_weights(latest)
loss, acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 91us/sample - loss: 0.4783 - accuracy: 0.8780
Restored model, accuracy: 87.80%

 

これらのファイルは何でしょう?

上のコードは重みを (訓練された重みだけをバイナリ形式で含む) チェックポイント形式ファイルのコレクションにストアします。チェックポイントは次を含みます : * モデルの重みを含む一つまたはそれ以上のシャード。* どの重みがどのシャードにストアされているかを指し示すインデックスファイル。

単一のマシン上でモデルを訓練しているだけであれば、サフィックス: .data-00000-of-00001 を持つ一つのシャードを持つでしょう。

 

重みを手動でセーブする

上で重みをどのようにモデルにロードするかを見ました。

手動で重みをセーブするのは同様に単純で、Model.save_weights メソッドを使用します。

# Save the weights
model.save_weights('./checkpoints/my_checkpoint')

# Restore the weights
model = create_model()
model.load_weights('./checkpoints/my_checkpoint')

loss,acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 99us/sample - loss: 0.4783 - accuracy: 0.8780
Restored model, accuracy: 87.80%

 

モデル全体をセーブする

モデルと optimizer はそれらの状態 (重みと変数) とモデル構成 (= configuration) の両者を含むファイルにセーブできます。これはモデルをエクスポートして元の python コードへのアクセスなしに使用できることを可能にします。optimizer-状態がリカバーされますので正確に貴方がやめたところから訓練を再開することさえできます。

完全に機能するモデルのセーブは非常に有用です — それらを TensorFlow.js にロード (HDF5, Saved Model) してから web ブラウザでそれらを訓練して実行することができます、あるいは TensorFlow Lite を使用して (HDF5, Saved Model) モバイルデバイス上で実行するためそれらを変換できます。

 

HDF5 ファイルとして

Keras は標準 HDF5 を使用した基本的なセーブ・フォーマットを提供します。私達の目的のためには、セーブされたモデルは単一のバイナリ・ブロブとして扱うことができます。

model = create_model()

model.fit(train_images, train_labels, epochs=5)

# Save entire model to a HDF5 file
model.save('my_model.h5')
Epoch 1/5
1000/1000 [==============================] - 0s 196us/sample - loss: 1.1492 - accuracy: 0.6900
Epoch 2/5
1000/1000 [==============================] - 0s 110us/sample - loss: 0.4192 - accuracy: 0.8810
Epoch 3/5
1000/1000 [==============================] - 0s 120us/sample - loss: 0.2916 - accuracy: 0.9240
Epoch 4/5
1000/1000 [==============================] - 0s 123us/sample - loss: 0.2059 - accuracy: 0.9540
Epoch 5/5
1000/1000 [==============================] - 0s 127us/sample - loss: 0.1531 - accuracy: 0.9680

さてそのファイルからモデルを再作成します :

# Recreate the exact same model, including weights and optimizer.
new_model = keras.models.load_model('my_model.h5')
new_model.summary()
Model: "sequential_6"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_12 (Dense)             (None, 512)               401920    
_________________________________________________________________
dropout_6 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_13 (Dense)             (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

その精度を確認します :

loss, acc = new_model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 100us/sample - loss: 0.4323 - accuracy: 0.8540
Restored model, accuracy: 85.40%

このテクニックは総てをセーブします :

  • 重み値。
  • モデルの構成 (= configuration) (アーキテクチャ)
  • optimizer 構成

Keras はアーキテクチャを調べてモデルをセーブします。現在、TensorFlow optimizer (from tf.train) をセーブすることはできません。それらを使用するときはモデルをロードした後で再コンパイルする必要があり、そして optimizer の状態を失うでしょう。

 

saved_model として

Caution: tf.keras モデルをセーブするこのメソッドは実験的で将来的なバージョンでは変更されるかもしれません。

新しいモデルを構築します :

model = create_model()

model.fit(train_images, train_labels, epochs=5)
Epoch 1/5
1000/1000 [==============================] - 0s 185us/sample - loss: 1.1505 - accuracy: 0.6660
Epoch 2/5
1000/1000 [==============================] - 0s 118us/sample - loss: 0.4217 - accuracy: 0.8760
Epoch 3/5
1000/1000 [==============================] - 0s 116us/sample - loss: 0.2763 - accuracy: 0.9270
Epoch 4/5
1000/1000 [==============================] - 0s 120us/sample - loss: 0.2071 - accuracy: 0.9460
Epoch 5/5
1000/1000 [==============================] - 0s 119us/sample - loss: 0.1497 - accuracy: 0.9690

<tensorflow.python.keras.callbacks.History at 0x7fb040483898>

saved_model を作成して、それをタイムスタンプされたディレクトリに置きます :

import time
saved_model_path = "./saved_models/{}".format(int(time.time()))

tf.keras.experimental.export_saved_model(model, saved_model_path)
saved_model_path
WARNING: Logging before flag parsing goes to stderr.
W0307 18:47:44.924113 140398839080704 deprecation.py:323] From /usr/local/lib/python3.5/dist-packages/tensorflow/python/saved_model/signature_def_utils_impl.py:253: build_tensor_info (from tensorflow.python.saved_model.utils_impl) is deprecated and will be removed in a future version.
Instructions for updating:
This function will only be available through the v1 compatibility library as tf.compat.v1.saved_model.utils.build_tensor_info or tf.compat.v1.saved_model.build_tensor_info.
W0307 18:47:44.926080 140398839080704 tf_logging.py:161] Export includes no default signature!
W0307 18:47:45.413053 140398839080704 tf_logging.py:161] Export includes no default signature!

'./saved_models/1551984464'

貴方の saved models をリスティングします :

!ls saved_models/
1551984464

saved model から新鮮な (= fresh) keras モデルを再ロードします。

new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)
new_model.summary()
Model: "sequential_7"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_14 (Dense)             (None, 512)               401920    
_________________________________________________________________
dropout_7 (Dropout)          (None, 512)               0         
_________________________________________________________________
dense_15 (Dense)             (None, 10)                5130      
=================================================================
Total params: 407,050
Trainable params: 407,050
Non-trainable params: 0
_________________________________________________________________

復元されたモデルを実行します。

model.predict(test_images).shape
(1000, 10)
# The model has to be compiled before evaluating.
# This step is not required if the saved model is only being deployed.

new_model.compile(optimizer=model.optimizer,  # keep the optimizer that was loaded
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# Evaluate the restored model.
loss, acc = new_model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))
1000/1000 [==============================] - 0s 94us/sample - loss: 0.4281 - accuracy: 0.8560
Restored model, accuracy: 85.60%

 

What’s Next

tf.keras によるセーブとロードへの簡単なガイドでした。

  • tf.keras ガイド は tf.keras によるモデルのセーブとロードについて更に示します。
  • eager execution の間のセーブについては Saving in eager を見てください。
  • Save and Restore ガイドは TensorFlow セービングについての下位詳細を持ちます。
     

    以上



TensorFlow 2.0 Alpha : Tutorials : ML 基本 :- overfitting と underfitting を調査する

TensorFlow 2.0 Alpha : Beginner Tutorials : ML 基本 :- overfitting と underfitting を調査する (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/30/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- overfitting と underfitting を調査する

いつものように、このサンプルのコードは tf.keras API を使用します、これについては TensorFlow Keras guide で更に学習できます。

前の例の両者 — 映画レビューの分類と燃費効率の予測 — では、検証データ上のモデルの精度が幾つかのエポックの訓練後に最大になり、それから減少し始めることを見ました。

換言すれば、モデルは訓練データに overfit しています。overfitting にどのように対処するかを学ぶことは重要です。訓練セット上で高い精度を獲得することはしばしば可能ですが、本当に望むことはテストデータ (あるいは前にまだ見ていないデータ) に上手く一般化されたモデルを開発することです。

overfitting の反対は underfitting です。underfitting は テストデータ上でまだ改善の余地があるときに発生します。これは幾つかの理由で起こります : モデルが十分にパワフルでない場合、過剰に正則化されている、あるいは単に十分に長く訓練されていない場合。これはネットワークが訓練データ内の関連するパターンを学習していないことを意味しています。

けれども長すぎる訓練をする場合、モデルは overfit し始めて訓練データからテストデータに一般化されないパターンを学習するでしょう。上手くバランスを取る必要があります。どのように適切な数のエポックの間訓練するかを理解することは下で探究するように有用なスキルです。

overfitting を回避するためには、最善の解法はより多くの訓練データを使用することです。より多くのデータ上で訓練されたモデルは自然により良く一般化されます。それが最早可能ではないとき、次善の解法は正則化のようなテクニックを使用することです。これらはモデルがストアできる情報の量とタイプに制約を課します。ネットワークが小さい数のパターンを記憶するだけの余裕がある場合、最適化プロセスはそれに最も目立つパターンに注目するように強制します、これは上手く一般化するより良い可能性を持ちます。

このノートブックでは、2 つの一般的な正則化テクニックを探究します — 重み正則化と dropout です — そしてそれらを使用して IMDB 映画レビュー分類ノートブックを改良します。

from __future__ import absolute_import, division, print_function

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf
from tensorflow import keras

import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.0.0-alpha0

 

IMDB データセットをダウンロードする

前のノートブックのように埋め込みを使用するのではなく、ここではセンテンスを multi-hot エンコードします。このモデルは訓練セットに迅速に overfit します。それは overfitting が発生したとき、それとどのように戦うかを実演するために使用されます。

リストの multi-hot エンコーディングはそれらを 0 と 1 のベクトルに変えることを意味します。具体的には、これは例えばシークエンス [3, 5] を (1 となる) インデックス 3 と 5 を除いて総て 0 の 10,000 次元ベクトルに変えることを意味します。

NUM_WORDS = 10000

(train_data, train_labels), (test_data, test_labels) = keras.datasets.imdb.load_data(num_words=NUM_WORDS)

def multi_hot_sequences(sequences, dimension):
    # Create an all-zero matrix of shape (len(sequences), dimension)
    results = np.zeros((len(sequences), dimension))
    for i, word_indices in enumerate(sequences):
        results[i, word_indices] = 1.0  # set specific indices of results[i] to 1s
    return results


train_data = multi_hot_sequences(train_data, dimension=NUM_WORDS)
test_data = multi_hot_sequences(test_data, dimension=NUM_WORDS)
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/imdb.npz
17465344/17464789 [==============================] - 0s 0us/step

結果としての multi-hot ベクトルの一つを見てみましょう。単語インデックスは頻度でソートされますので、プロットで見て取れるように、インデックス 0 の近くにはより多くの 1-値があることが予想されます :

plt.plot(train_data[0])
[]

 

overfitting を実演する

overfitting を回避する最も単純な方法はモデルのサイズ, i.e. モデルの学習可能なパラメータの数 (これは層数と層毎のユニット数により決定されます) を減じることです。深層学習では、モデルの学習可能なパラメータ数はしばしばモデルの “capacity” として参照されます。直感的には、より多くのパラメータを持つモデルはより多くの “記憶 capacity” を持ちそしてそれ故に訓練サンプルとターゲットの間の完全な辞書ライクなマッピング、どのような一般化パワーもないマッピングを容易に学習可能です、しかしこれは以前に見ていないデータ上で予測を行なうときに役に立ちません。

これを常に念頭に置いてください : 深層学習モデルは訓練データに fit するには良い傾向がありますが、実際の挑戦は一般化であり、fitting ではありません。

その一方で、ネットワークが制限された記憶リソースを持つ場合には、マッピングを容易には学習することができないでしょう。損失を最小化するためには、より予測力を持つ圧縮された (= compressed) 表現を学習しなければなりません。同時に、モデルを小さくし過ぎれば、それは訓練データに fit することが困難になるでしょう。これは “too much capacity” と “not enough capacity” の間のバランスです。

不幸なことに、(層の数や各層のための正しいサイズの視点から) モデルの正しいサイズやアーキテクチャ を決定するための魔法の公式はありません。異なるアーキテクチャのシリーズを使用して実験しなければなりません。

適切なモデルサイズを見つけるためには、比較的少ない層とパラメータで始めて、それから検証損失上の戻しが減衰し始めるまで層のサイズを増やしたり新しい層を追加します。これを映画レビュー分類ネットワークで試してみましょう。

ベースラインとして Dense 層だけを使用する単純なモデルを作成します、それからより小さいものと大きいバージョンを作成し、それらを比較してみます。

 

ベースライン・モデルを作成する

baseline_model = keras.Sequential([
    # `input_shape` is only required here so that `.summary` works. 
    keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

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

baseline_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 16)                160016    
_________________________________________________________________
dense_1 (Dense)              (None, 16)                272       
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 17        
=================================================================
Total params: 160,305
Trainable params: 160,305
Non-trainable params: 0
_________________________________________________________________
baseline_history = baseline_model.fit(train_data,
                                      train_labels,
                                      epochs=20,
                                      batch_size=512,
                                      validation_data=(test_data, test_labels),
                                      verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 4s - loss: 0.4881 - accuracy: 0.7869 - binary_crossentropy: 0.4881 - val_loss: 0.3445 - val_accuracy: 0.8765 - val_binary_crossentropy: 0.3445
Epoch 2/20
25000/25000 - 3s - loss: 0.2552 - accuracy: 0.9138 - binary_crossentropy: 0.2552 - val_loss: 0.2867 - val_accuracy: 0.8894 - val_binary_crossentropy: 0.2867
Epoch 3/20
25000/25000 - 3s - loss: 0.1864 - accuracy: 0.9357 - binary_crossentropy: 0.1864 - val_loss: 0.2899 - val_accuracy: 0.8835 - val_binary_crossentropy: 0.2899
Epoch 4/20
25000/25000 - 3s - loss: 0.1501 - accuracy: 0.9492 - binary_crossentropy: 0.1501 - val_loss: 0.3181 - val_accuracy: 0.8760 - val_binary_crossentropy: 0.3181
Epoch 5/20
25000/25000 - 3s - loss: 0.1230 - accuracy: 0.9598 - binary_crossentropy: 0.1230 - val_loss: 0.3308 - val_accuracy: 0.8756 - val_binary_crossentropy: 0.3308
Epoch 6/20
25000/25000 - 3s - loss: 0.1009 - accuracy: 0.9698 - binary_crossentropy: 0.1009 - val_loss: 0.3600 - val_accuracy: 0.8728 - val_binary_crossentropy: 0.3600
Epoch 7/20
25000/25000 - 3s - loss: 0.0832 - accuracy: 0.9764 - binary_crossentropy: 0.0832 - val_loss: 0.3924 - val_accuracy: 0.8679 - val_binary_crossentropy: 0.3924
Epoch 8/20
25000/25000 - 3s - loss: 0.0695 - accuracy: 0.9823 - binary_crossentropy: 0.0695 - val_loss: 0.4238 - val_accuracy: 0.8645 - val_binary_crossentropy: 0.4238
Epoch 9/20
25000/25000 - 3s - loss: 0.0554 - accuracy: 0.9876 - binary_crossentropy: 0.0554 - val_loss: 0.4602 - val_accuracy: 0.8628 - val_binary_crossentropy: 0.4602
Epoch 10/20
25000/25000 - 3s - loss: 0.0451 - accuracy: 0.9910 - binary_crossentropy: 0.0451 - val_loss: 0.5070 - val_accuracy: 0.8604 - val_binary_crossentropy: 0.5070
Epoch 11/20
25000/25000 - 3s - loss: 0.0345 - accuracy: 0.9944 - binary_crossentropy: 0.0345 - val_loss: 0.5511 - val_accuracy: 0.8581 - val_binary_crossentropy: 0.5511
Epoch 12/20
25000/25000 - 3s - loss: 0.0257 - accuracy: 0.9964 - binary_crossentropy: 0.0257 - val_loss: 0.5937 - val_accuracy: 0.8567 - val_binary_crossentropy: 0.5937
Epoch 13/20
25000/25000 - 3s - loss: 0.0191 - accuracy: 0.9984 - binary_crossentropy: 0.0191 - val_loss: 0.6493 - val_accuracy: 0.8544 - val_binary_crossentropy: 0.6493
Epoch 14/20
25000/25000 - 3s - loss: 0.0131 - accuracy: 0.9990 - binary_crossentropy: 0.0131 - val_loss: 0.7073 - val_accuracy: 0.8537 - val_binary_crossentropy: 0.7073
Epoch 15/20
25000/25000 - 3s - loss: 0.0093 - accuracy: 0.9993 - binary_crossentropy: 0.0093 - val_loss: 0.7430 - val_accuracy: 0.8540 - val_binary_crossentropy: 0.7430
Epoch 16/20
25000/25000 - 3s - loss: 0.0068 - accuracy: 0.9995 - binary_crossentropy: 0.0068 - val_loss: 0.7921 - val_accuracy: 0.8532 - val_binary_crossentropy: 0.7921
Epoch 17/20
25000/25000 - 3s - loss: 0.0052 - accuracy: 0.9995 - binary_crossentropy: 0.0052 - val_loss: 0.8222 - val_accuracy: 0.8529 - val_binary_crossentropy: 0.8222
Epoch 18/20
25000/25000 - 3s - loss: 0.0039 - accuracy: 0.9998 - binary_crossentropy: 0.0039 - val_loss: 0.8674 - val_accuracy: 0.8530 - val_binary_crossentropy: 0.8674
Epoch 19/20
25000/25000 - 3s - loss: 0.0030 - accuracy: 0.9998 - binary_crossentropy: 0.0030 - val_loss: 0.8911 - val_accuracy: 0.8528 - val_binary_crossentropy: 0.8911
Epoch 20/20
25000/25000 - 3s - loss: 0.0024 - accuracy: 0.9999 - binary_crossentropy: 0.0024 - val_loss: 0.9194 - val_accuracy: 0.8529 - val_binary_crossentropy: 0.9194

 

より小さいモデルを作成する

作成したばかりのベースライン・モデルに対して比較するためにより少ない隠れユニットを持つモデルを作成しましょう :

smaller_model = keras.Sequential([
    keras.layers.Dense(4, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

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

smaller_model.summary()
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_3 (Dense)              (None, 4)                 40004     
_________________________________________________________________
dense_4 (Dense)              (None, 4)                 20        
_________________________________________________________________
dense_5 (Dense)              (None, 1)                 5         
=================================================================
Total params: 40,029
Trainable params: 40,029
Non-trainable params: 0
_________________________________________________________________

そして同じデータを使用してモデルを訓練します :

smaller_history = smaller_model.fit(train_data,
                                    train_labels,
                                    epochs=20,
                                    batch_size=512,
                                    validation_data=(test_data, test_labels),
                                    verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 4s - loss: 0.6651 - accuracy: 0.6075 - binary_crossentropy: 0.6651 - val_loss: 0.6323 - val_accuracy: 0.6588 - val_binary_crossentropy: 0.6323
Epoch 2/20
25000/25000 - 3s - loss: 0.5969 - accuracy: 0.7276 - binary_crossentropy: 0.5969 - val_loss: 0.5779 - val_accuracy: 0.7404 - val_binary_crossentropy: 0.5779
Epoch 3/20
25000/25000 - 4s - loss: 0.5420 - accuracy: 0.8007 - binary_crossentropy: 0.5420 - val_loss: 0.5358 - val_accuracy: 0.7846 - val_binary_crossentropy: 0.5358
Epoch 4/20
25000/25000 - 3s - loss: 0.4982 - accuracy: 0.8413 - binary_crossentropy: 0.4982 - val_loss: 0.5023 - val_accuracy: 0.8180 - val_binary_crossentropy: 0.5023
Epoch 5/20
25000/25000 - 3s - loss: 0.4624 - accuracy: 0.8704 - binary_crossentropy: 0.4624 - val_loss: 0.4785 - val_accuracy: 0.8241 - val_binary_crossentropy: 0.4785
Epoch 6/20
25000/25000 - 3s - loss: 0.4327 - accuracy: 0.8883 - binary_crossentropy: 0.4327 - val_loss: 0.4543 - val_accuracy: 0.8570 - val_binary_crossentropy: 0.4543
Epoch 7/20
25000/25000 - 4s - loss: 0.4072 - accuracy: 0.9032 - binary_crossentropy: 0.4072 - val_loss: 0.4391 - val_accuracy: 0.8553 - val_binary_crossentropy: 0.4391
Epoch 8/20
25000/25000 - 4s - loss: 0.3849 - accuracy: 0.9134 - binary_crossentropy: 0.3849 - val_loss: 0.4245 - val_accuracy: 0.8640 - val_binary_crossentropy: 0.4245
Epoch 9/20
25000/25000 - 4s - loss: 0.3652 - accuracy: 0.9212 - binary_crossentropy: 0.3652 - val_loss: 0.4157 - val_accuracy: 0.8619 - val_binary_crossentropy: 0.4157
Epoch 10/20
25000/25000 - 3s - loss: 0.3476 - accuracy: 0.9284 - binary_crossentropy: 0.3476 - val_loss: 0.4044 - val_accuracy: 0.8705 - val_binary_crossentropy: 0.4044
Epoch 11/20
25000/25000 - 4s - loss: 0.3314 - accuracy: 0.9353 - binary_crossentropy: 0.3314 - val_loss: 0.3962 - val_accuracy: 0.8728 - val_binary_crossentropy: 0.3962
Epoch 12/20
25000/25000 - 4s - loss: 0.3166 - accuracy: 0.9405 - binary_crossentropy: 0.3166 - val_loss: 0.3888 - val_accuracy: 0.8744 - val_binary_crossentropy: 0.3888
Epoch 13/20
25000/25000 - 3s - loss: 0.3027 - accuracy: 0.9452 - binary_crossentropy: 0.3027 - val_loss: 0.3843 - val_accuracy: 0.8742 - val_binary_crossentropy: 0.3843
Epoch 14/20
25000/25000 - 4s - loss: 0.2900 - accuracy: 0.9494 - binary_crossentropy: 0.2900 - val_loss: 0.3812 - val_accuracy: 0.8733 - val_binary_crossentropy: 0.3812
Epoch 15/20
25000/25000 - 4s - loss: 0.2779 - accuracy: 0.9530 - binary_crossentropy: 0.2779 - val_loss: 0.3777 - val_accuracy: 0.8738 - val_binary_crossentropy: 0.3777
Epoch 16/20
25000/25000 - 3s - loss: 0.2666 - accuracy: 0.9558 - binary_crossentropy: 0.2666 - val_loss: 0.3730 - val_accuracy: 0.8744 - val_binary_crossentropy: 0.3730
Epoch 17/20
25000/25000 - 4s - loss: 0.2558 - accuracy: 0.9588 - binary_crossentropy: 0.2558 - val_loss: 0.3748 - val_accuracy: 0.8724 - val_binary_crossentropy: 0.3748
Epoch 18/20
25000/25000 - 4s - loss: 0.2458 - accuracy: 0.9613 - binary_crossentropy: 0.2458 - val_loss: 0.3707 - val_accuracy: 0.8744 - val_binary_crossentropy: 0.3707
Epoch 19/20
25000/25000 - 4s - loss: 0.2363 - accuracy: 0.9632 - binary_crossentropy: 0.2363 - val_loss: 0.3680 - val_accuracy: 0.8750 - val_binary_crossentropy: 0.3680
Epoch 20/20
25000/25000 - 4s - loss: 0.2276 - accuracy: 0.9655 - binary_crossentropy: 0.2276 - val_loss: 0.3654 - val_accuracy: 0.8755 - val_binary_crossentropy: 0.3654

 

より大きいモデルを作成する

エクササイズとして、より大きいモデルさえも作成して、それがどのように素早く overfitting し始めるかを見ることができます。次に、このベンチマークに、問題が必要とするよりも遥かにより大きい capacity を持つネットワークを追加しましょう :

bigger_model = keras.models.Sequential([
    keras.layers.Dense(512, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(512, activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

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

bigger_model.summary()
Model: "sequential_2"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense_6 (Dense)              (None, 512)               5120512   
_________________________________________________________________
dense_7 (Dense)              (None, 512)               262656    
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 513       
=================================================================
Total params: 5,383,681
Trainable params: 5,383,681
Non-trainable params: 0
_________________________________________________________________

そして、再度、同じデータを使用してモデルを訓練します :

bigger_history = bigger_model.fit(train_data, train_labels,
                                  epochs=20,
                                  batch_size=512,
                                  validation_data=(test_data, test_labels),
                                  verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 6s - loss: 0.3436 - accuracy: 0.8533 - binary_crossentropy: 0.3436 - val_loss: 0.2970 - val_accuracy: 0.8774 - val_binary_crossentropy: 0.2970
Epoch 2/20
25000/25000 - 5s - loss: 0.1419 - accuracy: 0.9487 - binary_crossentropy: 0.1419 - val_loss: 0.3333 - val_accuracy: 0.8716 - val_binary_crossentropy: 0.3333
Epoch 3/20
25000/25000 - 5s - loss: 0.0466 - accuracy: 0.9869 - binary_crossentropy: 0.0466 - val_loss: 0.4502 - val_accuracy: 0.8680 - val_binary_crossentropy: 0.4502
Epoch 4/20
25000/25000 - 5s - loss: 0.0068 - accuracy: 0.9988 - binary_crossentropy: 0.0068 - val_loss: 0.6078 - val_accuracy: 0.8658 - val_binary_crossentropy: 0.6078
Epoch 5/20
25000/25000 - 5s - loss: 8.2359e-04 - accuracy: 1.0000 - binary_crossentropy: 8.2359e-04 - val_loss: 0.6902 - val_accuracy: 0.8698 - val_binary_crossentropy: 0.6902
Epoch 6/20
25000/25000 - 5s - loss: 2.1378e-04 - accuracy: 1.0000 - binary_crossentropy: 2.1378e-04 - val_loss: 0.7351 - val_accuracy: 0.8700 - val_binary_crossentropy: 0.7351
Epoch 7/20
25000/25000 - 6s - loss: 1.3067e-04 - accuracy: 1.0000 - binary_crossentropy: 1.3067e-04 - val_loss: 0.7644 - val_accuracy: 0.8704 - val_binary_crossentropy: 0.7644
Epoch 8/20
25000/25000 - 6s - loss: 9.4273e-05 - accuracy: 1.0000 - binary_crossentropy: 9.4273e-05 - val_loss: 0.7877 - val_accuracy: 0.8700 - val_binary_crossentropy: 0.7877
Epoch 9/20
25000/25000 - 5s - loss: 7.2173e-05 - accuracy: 1.0000 - binary_crossentropy: 7.2173e-05 - val_loss: 0.8066 - val_accuracy: 0.8703 - val_binary_crossentropy: 0.8066
Epoch 10/20
25000/25000 - 5s - loss: 5.7300e-05 - accuracy: 1.0000 - binary_crossentropy: 5.7300e-05 - val_loss: 0.8235 - val_accuracy: 0.8704 - val_binary_crossentropy: 0.8235
Epoch 11/20
25000/25000 - 5s - loss: 4.6368e-05 - accuracy: 1.0000 - binary_crossentropy: 4.6368e-05 - val_loss: 0.8400 - val_accuracy: 0.8700 - val_binary_crossentropy: 0.8400
Epoch 12/20
25000/25000 - 5s - loss: 3.8188e-05 - accuracy: 1.0000 - binary_crossentropy: 3.8188e-05 - val_loss: 0.8553 - val_accuracy: 0.8705 - val_binary_crossentropy: 0.8553
Epoch 13/20
25000/25000 - 5s - loss: 3.1415e-05 - accuracy: 1.0000 - binary_crossentropy: 3.1415e-05 - val_loss: 0.8704 - val_accuracy: 0.8709 - val_binary_crossentropy: 0.8704
Epoch 14/20
25000/25000 - 5s - loss: 2.6080e-05 - accuracy: 1.0000 - binary_crossentropy: 2.6080e-05 - val_loss: 0.8868 - val_accuracy: 0.8706 - val_binary_crossentropy: 0.8868
Epoch 15/20
25000/25000 - 5s - loss: 2.1638e-05 - accuracy: 1.0000 - binary_crossentropy: 2.1638e-05 - val_loss: 0.9030 - val_accuracy: 0.8702 - val_binary_crossentropy: 0.9030
Epoch 16/20
25000/25000 - 5s - loss: 1.8066e-05 - accuracy: 1.0000 - binary_crossentropy: 1.8066e-05 - val_loss: 0.9198 - val_accuracy: 0.8702 - val_binary_crossentropy: 0.9198
Epoch 17/20
25000/25000 - 5s - loss: 1.5080e-05 - accuracy: 1.0000 - binary_crossentropy: 1.5080e-05 - val_loss: 0.9362 - val_accuracy: 0.8701 - val_binary_crossentropy: 0.9362
Epoch 18/20
25000/25000 - 5s - loss: 1.2686e-05 - accuracy: 1.0000 - binary_crossentropy: 1.2686e-05 - val_loss: 0.9524 - val_accuracy: 0.8700 - val_binary_crossentropy: 0.9524
Epoch 19/20
25000/25000 - 5s - loss: 1.0747e-05 - accuracy: 1.0000 - binary_crossentropy: 1.0747e-05 - val_loss: 0.9681 - val_accuracy: 0.8702 - val_binary_crossentropy: 0.9681
Epoch 20/20
25000/25000 - 5s - loss: 9.1827e-06 - accuracy: 1.0000 - binary_crossentropy: 9.1827e-06 - val_loss: 0.9834 - val_accuracy: 0.8702 - val_binary_crossentropy: 0.9834

 

訓練と検証損失をプロットする

実践は訓練損失を示し、破線は検証損失を表示します (remember: より低い検証損失はより良いモデルを示します)。ここで、より小さいネットワークはベースラインモデルよりも後で overfitting し始めて (4 ではなくて 6 エポック後) そしてそのパフォーマンスはひとたび overfitting し始めれば遥かによりゆっくりと低下します。

def plot_history(histories, key='binary_crossentropy'):
  plt.figure(figsize=(16,10))
    
  for name, history in histories:
    val = plt.plot(history.epoch, history.history['val_'+key],
                   '--', label=name.title()+' Val')
    plt.plot(history.epoch, history.history[key], color=val[0].get_color(),
             label=name.title()+' Train')

  plt.xlabel('Epochs')
  plt.ylabel(key.replace('_',' ').title())
  plt.legend()

  plt.xlim([0,max(history.epoch)])


plot_history([('baseline', baseline_history),
              ('smaller', smaller_history),
              ('bigger', bigger_history)])

より大きいネットワークは殆ど直ちに、丁度 1 エポック後に overfitting し始めます、そして遥かにより厳しく overfit することが分かるでしょう。ネットワークがより多くの capacity を持つほど、より迅速に訓練データをモデリングすることが可能です (低い訓練損失という結果になります) が、それはより overfitting しやすいです (訓練と検証損失間の巨大な差異という結果になります) 。

 

overfitting を回避するためのストラテジー

重み正則化を追加する

貴方はオッカムの剃刀の原理を知っているかもしれません: 何かについて 2 つの説明が与えられたとき、最も正しい可能性がある説明は「単純な」もの、最小量の仮定をするものです。これはまたニューラルネットワークにより学習されたモデルにも適用されます : ある訓練データとネットワーク・アーキテクチャが与えられたとき、データを説明できる重み値の複数のセット (複数のモデル) があり、そしてより単純なモデルは複雑なものよりも overfit する可能性が低いです。

このコンテキストでの「単純なモデル」はそこではパラメータ値の分布がより少ないエントロピーを持ちます (あるいはまとめてより少ないパラメータを持つモデルです、上のセクションで見たような)。そのため overfitting を軽減する一般的な方法はその (ネットワークの) 重みを小さい値だけを取るように強制してネットワークの複雑さに制約を置くことです、それは重み値の分布をより「正則 (= regular)」にします。これは「重み正則化」と呼ばれ、それはネットワークの損失関数に巨大な重みを持つことに関連するコストを追加することによって成されます。このコストは 2 つのフレーバーに分けられます :

  • L1 正則化, そこでは追加されるコストは重み係数の絶対値への (i.e. 重みの「L1 ノルム」と呼称されるものへの) 比例項です。
  • L2 正則化, そこでは追加されるコストは重み係数の値の二乗への (i.e. 重みの「L2 ノルム」と呼称されるものへの) 比例項です。L2 正則化はまたニューラルネットワークのコンテキストでは重み減衰とも呼称されます。異なる名前に混乱してはいけません。重み減衰は数学的には L2 正則化と正確に同じです。

L1 正則化は重みパラメータの幾つかをゼロにするために疎性を導入します。L2 正則化は重みパラメータにそれらを疎にすることなくペナルティを課します – L2 がより一般的である一つの理由です。

tf.keras では、重み正則化は層にキーワード引数として重み regularizer インスタンスを渡すことにより追加されます。今は L2 重み正則化を追加しましょう。

l2_model = keras.models.Sequential([
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dense(16, kernel_regularizer=keras.regularizers.l2(0.001),
                       activation='relu'),
    keras.layers.Dense(1, activation='sigmoid')
])

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

l2_model_history = l2_model.fit(train_data, train_labels,
                                epochs=20,
                                batch_size=512,
                                validation_data=(test_data, test_labels),
                                verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 3s - loss: 0.4968 - accuracy: 0.8198 - binary_crossentropy: 0.4535 - val_loss: 0.3682 - val_accuracy: 0.8781 - val_binary_crossentropy: 0.3223
Epoch 2/20
25000/25000 - 3s - loss: 0.2942 - accuracy: 0.9117 - binary_crossentropy: 0.2448 - val_loss: 0.3376 - val_accuracy: 0.8865 - val_binary_crossentropy: 0.2857
Epoch 3/20
25000/25000 - 3s - loss: 0.2473 - accuracy: 0.9314 - binary_crossentropy: 0.1932 - val_loss: 0.3408 - val_accuracy: 0.8853 - val_binary_crossentropy: 0.2853
Epoch 4/20
25000/25000 - 3s - loss: 0.2260 - accuracy: 0.9414 - binary_crossentropy: 0.1690 - val_loss: 0.3592 - val_accuracy: 0.8786 - val_binary_crossentropy: 0.3010
Epoch 5/20
25000/25000 - 3s - loss: 0.2111 - accuracy: 0.9492 - binary_crossentropy: 0.1519 - val_loss: 0.3705 - val_accuracy: 0.8764 - val_binary_crossentropy: 0.3105
Epoch 6/20
25000/25000 - 3s - loss: 0.1995 - accuracy: 0.9542 - binary_crossentropy: 0.1385 - val_loss: 0.3833 - val_accuracy: 0.8748 - val_binary_crossentropy: 0.3218
Epoch 7/20
25000/25000 - 3s - loss: 0.1924 - accuracy: 0.9569 - binary_crossentropy: 0.1301 - val_loss: 0.4054 - val_accuracy: 0.8710 - val_binary_crossentropy: 0.3425
Epoch 8/20
25000/25000 - 3s - loss: 0.1863 - accuracy: 0.9588 - binary_crossentropy: 0.1227 - val_loss: 0.4172 - val_accuracy: 0.8703 - val_binary_crossentropy: 0.3532
Epoch 9/20
25000/25000 - 3s - loss: 0.1806 - accuracy: 0.9599 - binary_crossentropy: 0.1159 - val_loss: 0.4358 - val_accuracy: 0.8653 - val_binary_crossentropy: 0.3707
Epoch 10/20
25000/25000 - 3s - loss: 0.1761 - accuracy: 0.9630 - binary_crossentropy: 0.1106 - val_loss: 0.4463 - val_accuracy: 0.8663 - val_binary_crossentropy: 0.3803
Epoch 11/20
25000/25000 - 3s - loss: 0.1716 - accuracy: 0.9651 - binary_crossentropy: 0.1051 - val_loss: 0.4604 - val_accuracy: 0.8643 - val_binary_crossentropy: 0.3935
Epoch 12/20
25000/25000 - 3s - loss: 0.1684 - accuracy: 0.9646 - binary_crossentropy: 0.1012 - val_loss: 0.4770 - val_accuracy: 0.8617 - val_binary_crossentropy: 0.4095
Epoch 13/20
25000/25000 - 3s - loss: 0.1639 - accuracy: 0.9692 - binary_crossentropy: 0.0960 - val_loss: 0.4884 - val_accuracy: 0.8606 - val_binary_crossentropy: 0.4203
Epoch 14/20
25000/25000 - 3s - loss: 0.1617 - accuracy: 0.9682 - binary_crossentropy: 0.0933 - val_loss: 0.5072 - val_accuracy: 0.8582 - val_binary_crossentropy: 0.4385
Epoch 15/20
25000/25000 - 3s - loss: 0.1613 - accuracy: 0.9685 - binary_crossentropy: 0.0925 - val_loss: 0.5256 - val_accuracy: 0.8587 - val_binary_crossentropy: 0.4563
Epoch 16/20
25000/25000 - 3s - loss: 0.1597 - accuracy: 0.9689 - binary_crossentropy: 0.0895 - val_loss: 0.5360 - val_accuracy: 0.8546 - val_binary_crossentropy: 0.4656
Epoch 17/20
25000/25000 - 3s - loss: 0.1575 - accuracy: 0.9701 - binary_crossentropy: 0.0873 - val_loss: 0.5422 - val_accuracy: 0.8560 - val_binary_crossentropy: 0.4719
Epoch 18/20
25000/25000 - 3s - loss: 0.1559 - accuracy: 0.9714 - binary_crossentropy: 0.0851 - val_loss: 0.5778 - val_accuracy: 0.8516 - val_binary_crossentropy: 0.5069
Epoch 19/20
25000/25000 - 3s - loss: 0.1561 - accuracy: 0.9693 - binary_crossentropy: 0.0849 - val_loss: 0.5652 - val_accuracy: 0.8543 - val_binary_crossentropy: 0.4936
Epoch 20/20
25000/25000 - 3s - loss: 0.1490 - accuracy: 0.9750 - binary_crossentropy: 0.0775 - val_loss: 0.5752 - val_accuracy: 0.8532 - val_binary_crossentropy: 0.5040

l2(0.001) は層の重み行列の総ての係数が 0.001 * weight_coefficient_value**2 をネットワークの総計損失に追加することを意味します。このペナルティは訓練時のみに追加されますので、このネットワークに対する損失はテスト時よりも訓練時に遥かに高くなることに注意してください。

L2 正則化ペナルティのインパクトがここにあります :

plot_history([('baseline', baseline_history),
              ('l2', l2_model_history)])

見て取れるように、両者のモデルが同じパラメータ数を持つ場合でさえも、L2 正則化されたモデルはベースライン・モデルよりも overfitting に大して遥かにより耐性があります。

 

dropout を追加する

Dropout は最も効果的で最も一般に使用されるニューラルネットワークのための正則化テクニックの一つで、University of Toronto の Hinton とその学生により開発されました。Dropout は層に適用され、訓練の間層の出力特徴の数をランダムに “dropping out” (i.e. ゼロに設定) することから成ります。与えられた層は訓練の間に与えられた入力サンプルに対して通常はベクトル [0.2, 0.5, 1.3, 0.8, 1.1] を返したと仮定します ; dropout を適用した後、このベクトルはランダムに分布する幾つかのゼロ要素を持つでしょう、e.g. [0, 0.5, 1.3, 0, 1.1] です。”dropout 率” はゼロにされる特徴の割合です ; それは通常は 0.2 と 0.5 の間に設定されます。テスト時には、ユニットは drop out されず、代わりに層の出力値は dropout 率に等しい倍数でスケールダウンされます、これは訓練時よりもより多いユニットが有効であるという事実のためにバランスを取るためです。

tf.keras では Dropout 層を通してネットワークに dropout を導入できます、それはすぐ前の層の出力に適用されます。

IMDB ネットワークに 2 つの Dropout 層をそれらがどのように上手く overfitting を減じるかを見るために追加しましょう :

dpt_model = keras.models.Sequential([
    keras.layers.Dense(16, activation='relu', input_shape=(NUM_WORDS,)),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(16, activation='relu'),
    keras.layers.Dropout(0.5),
    keras.layers.Dense(1, activation='sigmoid')
])

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

dpt_model_history = dpt_model.fit(train_data, train_labels,
                                  epochs=20,
                                  batch_size=512,
                                  validation_data=(test_data, test_labels),
                                  verbose=2)
Train on 25000 samples, validate on 25000 samples
Epoch 1/20
25000/25000 - 4s - loss: 0.6059 - accuracy: 0.6622 - binary_crossentropy: 0.6059 - val_loss: 0.4356 - val_accuracy: 0.8602 - val_binary_crossentropy: 0.4356
Epoch 2/20
25000/25000 - 3s - loss: 0.4259 - accuracy: 0.8214 - binary_crossentropy: 0.4259 - val_loss: 0.3207 - val_accuracy: 0.8828 - val_binary_crossentropy: 0.3207
Epoch 3/20
25000/25000 - 3s - loss: 0.3316 - accuracy: 0.8795 - binary_crossentropy: 0.3316 - val_loss: 0.2837 - val_accuracy: 0.8890 - val_binary_crossentropy: 0.2837
Epoch 4/20
25000/25000 - 3s - loss: 0.2761 - accuracy: 0.9049 - binary_crossentropy: 0.2761 - val_loss: 0.2753 - val_accuracy: 0.8887 - val_binary_crossentropy: 0.2753
Epoch 5/20
25000/25000 - 3s - loss: 0.2334 - accuracy: 0.9237 - binary_crossentropy: 0.2334 - val_loss: 0.2797 - val_accuracy: 0.8875 - val_binary_crossentropy: 0.2797
Epoch 6/20
25000/25000 - 3s - loss: 0.2039 - accuracy: 0.9351 - binary_crossentropy: 0.2039 - val_loss: 0.2908 - val_accuracy: 0.8849 - val_binary_crossentropy: 0.2908
Epoch 7/20
25000/25000 - 3s - loss: 0.1792 - accuracy: 0.9447 - binary_crossentropy: 0.1792 - val_loss: 0.3114 - val_accuracy: 0.8829 - val_binary_crossentropy: 0.3114
Epoch 8/20
25000/25000 - 3s - loss: 0.1625 - accuracy: 0.9490 - binary_crossentropy: 0.1625 - val_loss: 0.3249 - val_accuracy: 0.8826 - val_binary_crossentropy: 0.3249
Epoch 9/20
25000/25000 - 3s - loss: 0.1405 - accuracy: 0.9560 - binary_crossentropy: 0.1405 - val_loss: 0.3417 - val_accuracy: 0.8811 - val_binary_crossentropy: 0.3417
Epoch 10/20
25000/25000 - 3s - loss: 0.1256 - accuracy: 0.9621 - binary_crossentropy: 0.1256 - val_loss: 0.3836 - val_accuracy: 0.8799 - val_binary_crossentropy: 0.3836
Epoch 11/20
25000/25000 - 3s - loss: 0.1152 - accuracy: 0.9650 - binary_crossentropy: 0.1152 - val_loss: 0.3932 - val_accuracy: 0.8779 - val_binary_crossentropy: 0.3932
Epoch 12/20
25000/25000 - 3s - loss: 0.1007 - accuracy: 0.9691 - binary_crossentropy: 0.1007 - val_loss: 0.4139 - val_accuracy: 0.8785 - val_binary_crossentropy: 0.4139
Epoch 13/20
25000/25000 - 3s - loss: 0.0900 - accuracy: 0.9740 - binary_crossentropy: 0.0900 - val_loss: 0.4557 - val_accuracy: 0.8778 - val_binary_crossentropy: 0.4557
Epoch 14/20
25000/25000 - 3s - loss: 0.0872 - accuracy: 0.9749 - binary_crossentropy: 0.0872 - val_loss: 0.4421 - val_accuracy: 0.8747 - val_binary_crossentropy: 0.4421
Epoch 15/20
25000/25000 - 3s - loss: 0.0785 - accuracy: 0.9763 - binary_crossentropy: 0.0785 - val_loss: 0.4921 - val_accuracy: 0.8766 - val_binary_crossentropy: 0.4921
Epoch 16/20
25000/25000 - 3s - loss: 0.0709 - accuracy: 0.9787 - binary_crossentropy: 0.0709 - val_loss: 0.4958 - val_accuracy: 0.8731 - val_binary_crossentropy: 0.4958
Epoch 17/20
25000/25000 - 3s - loss: 0.0708 - accuracy: 0.9793 - binary_crossentropy: 0.0708 - val_loss: 0.5323 - val_accuracy: 0.8736 - val_binary_crossentropy: 0.5323
Epoch 18/20
25000/25000 - 3s - loss: 0.0672 - accuracy: 0.9802 - binary_crossentropy: 0.0672 - val_loss: 0.5363 - val_accuracy: 0.8740 - val_binary_crossentropy: 0.5363
Epoch 19/20
25000/25000 - 3s - loss: 0.0608 - accuracy: 0.9815 - binary_crossentropy: 0.0608 - val_loss: 0.5643 - val_accuracy: 0.8743 - val_binary_crossentropy: 0.5643
Epoch 20/20
25000/25000 - 3s - loss: 0.0609 - accuracy: 0.9817 - binary_crossentropy: 0.0609 - val_loss: 0.5689 - val_accuracy: 0.8734 - val_binary_crossentropy: 0.5689
plot_history([('baseline', baseline_history),
              ('dropout', dpt_model_history)])

dropout の追加はベースライン・モデルを超える明確な改善です。

復習のために: ここにニューラルネットワークで overfitting を回避するための最も一般的な方法があります :

  • より多くの訓練データを得る。
  • ネットワークの capcity を減じる。
  • 重み正則を追加する。
  • dropout を追加する。

そしてこのガイドでカバーされていない 2 つの重要なアプローチはデータ増強とバッチ正規化です。

 

以上



TensorFlow 2.0 Alpha : Tutorials : ML 基本 :- 燃費効率 : 回帰

TensorFlow 2.0 Alpha : Beginner Tutorials : ML 基本 :- 燃費効率 : 回帰 (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 03/29/2019

* 本ページは、TensorFlow の本家サイトの TF 2.0 Alpha – Beginner Tutorials – ML basics の以下のページを翻訳した上で
適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

ML 基本 :- 燃費効率 : 回帰

回帰問題では、価格や確率のような、連続値の出力を予測することを狙いとします。これを分類問題と対比すると、そこではクラスのリストからクラスを選択することを目的とします (例えば、写真がりんごかオレンジを含む、どのフルーツが写真にあるかを認識します)。

この notebook は古典的な Auto MPG データセットを使用して 1970s 末と 1980s 初期の自動車の燃費効率を予測するモデルを構築します。これを行なうために、その機関からの多くの自動車の記述を持つモデルを提供します。この記述は次のような属性を含みます : 気筒 (= cylinders), 排気量 (= displacement), 馬力 (= horsepower) そして重量 (= weight)。

このサンプルは tf.kreas API を使用します、詳細は このガイド を見てください。

# Use seaborn for pairplot
!pip install -q seaborn
from __future__ import absolute_import, division, print_function

import pathlib

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

!pip install -q tensorflow==2.0.0-alpha0
import tensorflow as tf

from tensorflow import keras
from tensorflow.keras import layers

print(tf.__version__)
2.0.0-alpha0

 

The Auto MPG データセット

データセットは UCI Machine Learning レポジトリ から利用可能です。

 

データを得る

最初にデータセットをダウンロードします。

dataset_path = keras.utils.get_file("auto-mpg.data", "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path
dataset_path = keras.utils.get_file("auto-mpg.data", "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data")
dataset_path

pandas を使用してそれをインポートします。

column_names = ['MPG','Cylinders','Displacement','Horsepower','Weight',
                'Acceleration', 'Model Year', 'Origin'] 
raw_dataset = pd.read_csv(dataset_path, names=column_names,
                      na_values = "?", comment='\t',
                      sep=" ", skipinitialspace=True)

dataset = raw_dataset.copy()
dataset.tail()

MPG Cylinders Displacement Horsepower Weight Acceleration Model Year Origin
393 27.0 4 140.0 86.0 2790.0 15.6 82 1
394 44.0 4 97.0 52.0 2130.0 24.6 82 2
395 32.0 4 135.0 84.0 2295.0 11.6 82 1
396 28.0 4 120.0 79.0 2625.0 18.6 82 1
397 31.0 4 119.0 82.0 2720.0 19.4 82 1

 

データをクリーンにする

データセットは幾つかの未知の値を含みます。

dataset.isna().sum()
MPG             0
Cylinders       0
Displacement    0
Horsepower      6
Weight          0
Acceleration    0
Model Year      0
Origin          0
dtype: int64

この初期チュートリアルを単純に維持するためにこれらの行を破棄します。

dataset = dataset.dropna()

“Origin” カラムは実際には categorical であり、numeric ではありません。そこでそれを one-hot に変換します :

origin = dataset.pop('Origin')
dataset['USA'] = (origin == 1)*1.0
dataset['Europe'] = (origin == 2)*1.0
dataset['Japan'] = (origin == 3)*1.0
dataset.tail()

MPG Cylinders Displacement Horsepower Weight Acceleration Model Year USA Europe Japan
393 27.0 4 140.0 86.0 2790.0 15.6 82 1.0 0.0 0.0
394 44.0 4 97.0 52.0 2130.0 24.6 82 0.0 1.0 0.0
395 32.0 4 135.0 84.0 2295.0 11.6 82 1.0 0.0 0.0
396 28.0 4 120.0 79.0 2625.0 18.6 82 1.0 0.0 0.0
397 31.0 4 119.0 82.0 2720.0 19.4 82 1.0 0.0 0.0

 

データを訓練とテストに分割する

さてデータセットを訓練セットとテストセットに分割します。

モデルの最後の評価でテストセットを使用します。

train_dataset = dataset.sample(frac=0.8,random_state=0)
test_dataset = dataset.drop(train_dataset.index)

 

データを調査する

訓練セットからカラムの 2,3 のペアの同時分布を簡単に見てみます。

sns.pairplot(train_dataset[["MPG", "Cylinders", "Displacement", "Weight"]], diag_kind="kde")
<seaborn.axisgrid.PairGrid at 0x7ff9fddcc438>

全体的な統計情報も見てみます :

train_stats = train_dataset.describe()
train_stats.pop("MPG")
train_stats = train_stats.transpose()
train_stats

count mean std min 25% 50% 75% max
Cylinders 314.0 5.477707 1.699788 3.0 4.00 4.0 8.00 8.0
Displacement 314.0 195.318471 104.331589 68.0 105.50 151.0 265.75 455.0
Horsepower 314.0 104.869427 38.096214 46.0 76.25 94.5 128.00 225.0
Weight 314.0 2990.251592 843.898596 1649.0 2256.50 2822.5 3608.00 5140.0
Acceleration 314.0 15.559236 2.789230 8.0 13.80 15.5 17.20 24.8
Model Year 314.0 75.898089 3.675642 70.0 73.00 76.0 79.00 82.0
USA 314.0 0.624204 0.485101 0.0 0.00 1.0 1.00 1.0
Europe 314.0 0.178344 0.383413 0.0 0.00 0.0 0.00 1.0
Japan 314.0 0.197452 0.398712 0.0 0.00 0.0 0.00 1.0

 

ラベルから特徴を分ける

特徴からターゲット値、あるいは「ラベル」を分離します。このラベルがモデルを訓練して予測する値です。

train_labels = train_dataset.pop('MPG')
test_labels = test_dataset.pop('MPG')

 

データを正規化する

上の train_stats ブロックを再度見て各特徴の範囲がどのように異なっているかに注意してください。

異なるスケールと範囲を使用する特徴を正規化することは良い実践です。特徴正規化なしでもモデルは収束するかもしれませんが、それは訓練をより困難にし、そしてそれは結果としてのモデルを入力で使用される単位の選択に依拠させます。

Note: これらの統計情報を訓練データセットだけから意図的に生成しますが、こららの統計情報はまたテストデータセットを正規化するためにも使用されます。テストデータセットを (モデルがその上で訓練されたのと) 同じ分布に射影するためにそれを行なう必要があります。

def norm(x):
  return (x - train_stats['mean']) / train_stats['std']
normed_train_data = norm(train_dataset)
normed_test_data = norm(test_dataset)

この正規化されたデータはモデルを訓練するために使用するものです。

Caution: ここで入力を正規化するために使用された統計情報 (平均と標準偏差) は、先に行なった one-hot エンコーディングと一緒に、モデルに供給されるどのような他のデータにも適用される必要があります。それはテストセットそしてプロダクションでモデルが使用されるときのライブデータを含みます。

 

モデル

モデルを構築する

私達のモデルを構築しましょう。ここで、2 つの密に結合した隠れ層、そして単一の連続値を返す出力層を持つ Sequential モデルを使用します。モデル構築ステップは関数, build_model にラップされます、何故ならば後で 2 番目のモデルを作成するからです。

def build_model():
  model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=[len(train_dataset.keys())]),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
  ])

  optimizer = tf.keras.optimizers.RMSprop(0.001)

  model.compile(loss='mse',
                optimizer=optimizer,
                metrics=['mae', 'mse'])
  return model
model = build_model()

 

モデルを調査する

モデルの単純な記述を表示出力する .summary メソッドを使用します。

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
dense (Dense)                (None, 64)                640       
_________________________________________________________________
dense_1 (Dense)              (None, 64)                4160      
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 65        
=================================================================
Total params: 4,865
Trainable params: 4,865
Non-trainable params: 0
_________________________________________________________________

さてモデルを試してみましょう。訓練データから 10 サンプルのバッチを取ってその上で model.predict を呼び出します。

example_batch = normed_train_data[:10]
example_result = model.predict(example_batch)
example_result
array([[ 0.3610134 ],
       [ 0.4167748 ],
       [ 0.12662223],
       [ 0.5402586 ],
       [-0.11418512],
       [ 0.33147115],
       [-0.02265942],
       [ 0.13972259],
       [ 0.24145225],
       [-0.18277109]], dtype=float32)

それは動作しているようです、そしてそれは期待される shape と type の結果を生成します。

 

モデルを訓練する

モデルを 1000 エポックの間訓練して訓練と検証精度を history オブジェクトに記録します。

# Display training progress by printing a single dot for each completed epoch
class PrintDot(keras.callbacks.Callback):
  def on_epoch_end(self, epoch, logs):
    if epoch % 100 == 0: print('')
    print('.', end='')

EPOCHS = 1000

history = model.fit(
  normed_train_data, train_labels,
  epochs=EPOCHS, validation_split = 0.2, verbose=0,
  callbacks=[PrintDot()])
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................
....................................................................................................

history オブジェクトにストアされている統計情報を使用してモデルの訓練進捗を可視化します。

hist = pd.DataFrame(history.history)
hist['epoch'] = history.epoch
hist.tail()

loss mae mse val_loss val_mae val_mse epoch
995 2.635363 1.023983 2.635363 10.875167 2.452306 10.875167 995
996 2.714065 1.100465 2.714065 9.788578 2.239394 9.788578 996
997 2.666638 1.052226 2.666638 10.569145 2.330731 10.569145 997
998 2.580438 1.035858 2.580438 10.350241 2.304158 10.350241 998
999 2.619640 1.072735 2.619640 10.937443 2.378247 10.937444 999

def plot_history(history):
  hist = pd.DataFrame(history.history)
  hist['epoch'] = history.epoch
  
  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Abs Error [MPG]')
  plt.plot(hist['epoch'], hist['mae'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mae'],
           label = 'Val Error')
  plt.ylim([0,5])
  plt.legend()
  
  plt.figure()
  plt.xlabel('Epoch')
  plt.ylabel('Mean Square Error [$MPG^2$]')
  plt.plot(hist['epoch'], hist['mse'],
           label='Train Error')
  plt.plot(hist['epoch'], hist['val_mse'],
           label = 'Val Error')
  plt.ylim([0,20])
  plt.legend()
  plt.show()


plot_history(history)

このグラフはおよそ 100 エポック後には検証エラーで殆ど改良しない、あるいは劣化さえ示します。検証スコアが改良しないとき訓練を自動的に停止するように model.fit 呼び出しを更新しましょう。EarlyStopping コールバックを使用します、これは総てのエポックのために訓練条件をテストします。エポックの設定された総量が改良を示すことなく経過する場合、訓練を自動的に停止します。

このコールバックについて ここで 更に学習できます。

model = build_model()

# The patience parameter is the amount of epochs to check for improvement
early_stop = keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)

history = model.fit(normed_train_data, train_labels, epochs=EPOCHS,
                    validation_split = 0.2, verbose=0, callbacks=[early_stop, PrintDot()])

plot_history(history)

................................................

グラフは検証セット上、平均エラーが通常は +/- 2 MPG まわりであることを示しています。これは良いでしょうか?その決定は貴方に委ねましょう。

モデルを訓練するときに使用しなかった、テストセット を使用してモデルがどの程度上手く一般化されたかを見てみましょう。これは、現実世界でモデルを使用するときどの程度上手く予測することをモデルに期待できるかを私達に教えてくれます。

loss, mae, mse = model.evaluate(normed_test_data, test_labels, verbose=0)

print("Testing set Mean Abs Error: {:5.2f} MPG".format(mae))
Testing set Mean Abs Error:  1.97 MPG

 

予測を行なう

最後に、テストセットのデータを使用してMPG 値を予測します :

test_predictions = model.predict(normed_test_data).flatten()

plt.scatter(test_labels, test_predictions)
plt.xlabel('True Values [MPG]')
plt.ylabel('Predictions [MPG]')
plt.axis('equal')
plt.axis('square')
plt.xlim([0,plt.xlim()[1]])
plt.ylim([0,plt.ylim()[1]])
_ = plt.plot([-100, 100], [-100, 100])

私達のモデルは適度に上手く予想しているようです。エラー分布を見てみましょう。

error = test_predictions - test_labels
plt.hist(error, bins = 25)
plt.xlabel("Prediction Error [MPG]")
_ = plt.ylabel("Count")

それは完全にはガウシアンではありませんが、それを期待できます、何故ならばサンプルの数が非常に小さいからです。

 

最後に

この notebook は回帰問題を扱う幾つかのテクニックを紹介しました。

  • Mean Squared Error (MSE) は回帰問題のために使用される一般的な損失関数です (異なる損失関数が分類問題に使われます)。
  • 同様に、回帰に使用される評価メトリクスは分類とは異なります。一般的な回帰メトリクスは Mean Absolute Error (MAE) です。
  • 数値入力データ特徴が異なる範囲の値を持つとき、各特徴は独立的に同じ範囲にスケールされるべきです。
  • それほど多くの訓練データがない場合、overfitting を回避するために一つのテクニックは少ない隠れ層を持つ小さいネットワークを選択することです。
  • Early stopping は overfitting を回避するための有用なテクニックです。
 

以上



AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com