長期的に報酬を最大化できるように環境のなかで行動を選択できるエージェントを作ることを目標とする機械学習.
強化学習の登場人物
エージェントが方策TTに基づき、環境に対して行動すると、環境の状態Sが変わる また、環境からエージェントに報酬がもらえる.
エージェントは報酬が最大になるように方策を見つけることが目標になる.
探索が足りないともっとベストな行動が取れないし、過去の経験を利用し続けているとよりより行動が取れないというトレードオフのこと
状態価値関数と行動価値関数がある. 主に行動価値関数が用いられる.
エージェントがある状態でどういう行動をするかを決めるための確率を与える関数.
方策反復法: 方策をモデル化して最適化する手法
$$ θ^{(t+1)} = θ^{(t)} + ϵ∇J(θ) $$ NNの重みの更新と似てる. Jは方策の良さを表す.
NNは誤差関数が小さくなるように学習をしていた. 強化学習では報酬を大きくなるように学習をする.
$$ ∇_θJ(θ) = ∇_θ \sum_{a \in A} π_θ(a | s)Q^π(s, a) = E_{π_θ} \left[ (∇_θlogπ_θ(a | s)Q^π(s, a)) \right] $$
ある行動を取った時の報酬を全ての行動に対して足し合わせて$θ$で微分している
状態行動価値Qを学習するためのアルゴリズムには以下がある.
Alpha Goの学習は以下のステップで行われる.
ネットワークにショートカット構造を追加して勾配爆発・消失をしないようにする
Convolution -> BatchNorm -> ReLU -> Convolution -> BatchNorm -> Add -> ReLU
(※1)を1ブロックとする基本構造を持つ.
層数の違うネットワークのアンサンブル効果が得られている.
Residual Blockの工夫
Newwork構造の工夫
根本的な深層学習のアイディアは、畳み込み、プーリング、RNN、attention、活性化関数であり基本のレゴパーツのようなものになっている.
AlphaGoZeroを凌ぐ、AlphaZero, MuZeroが2020年の段階で登場しているそう.
MuZeroでは先読みとモデルベースでの計画を実施している. モデルベースは環境のシミュレータをNN等でモデル化することで、シミュレータを使って計画を立て、実際の環境での計画と同じになるようにシミュレータを学習していく.
同期型・非同期型での学習方法がある.
現在の主流は精度が良い同期型による学習.
親モデルを各ワーカーに分割してそれぞれの分割したモデルを学習させる.学習が終わったら1つのモデルに復元する
分岐したモデルを別のワーカーで学習することが主流となっている.
GPGPU(General-purpose on GPU): グラフィック以外の用途で使用されるGPUの総称.
CPU: 少数精鋭でいろんな演算ができる. GPU: 特定の演算ができる. ニューラルネットの学習は単純な行列計算が多いので相性が良い.
モデルの重みをメモリに覚えておいて計算する必要がある。64bit浮動小数点数を32bitなどの下位の精度に落とすことでメモリと演算処理の削減を行う.
モデルの精度が落ちても16bitでの計算(〜150TeraFLOPS)が現実的に行われている.
精度の高い大きなモデルから軽量なモデルを作ること.
教師モデルと生徒モデルの2で構成される.
入力をすでに学習済の教師モデルと未学習の生徒モデルに入れる. 教師モデルと生徒モデルの誤差を生徒モデルに対して使用し、生徒モデルを学習していく.
ネットワークが大きくなると大量のパラメータになるが全てのニューロンの計算が精度に寄与しているわけでないため、モデル精度の寄与が少ないニューロンを削減してモデルの軽量化・高速化をする手法.
ニューロンの削減は重みが閾値以下の場合にニューロンを削減し再学習を行う.
実装演習でもGPUを使った高速化技術を使用している. 実装演習をGoogle colaboratoryを使用して実行している. Bertのモデルを実行していたとき、ランタイムのハードウェアアクセラレータがCPUでは全然学習が進まなかったのに対して、GPUに変えると学習が早く進んだ.
蒸留も実装演習で使用されている. LSTMよりもパラメータ数を減らしたGRUの方がずっと学習は早く済んでいる.
画像認識のニューラルネットワーク. 畳み込み演算を工夫している.Depthwise ConvolutionとPointwise Convolution.
一般的な畳み込みレイヤー
Depthwise Convolution
Pointwise Convolution
画像認識のニューラルネットワーク. Dense Blockでは出力層に前の層の入力を足し合わせていく.
Transition Layerでは、Dense Blockで大きくなったチャネルサイズを減らし、その後のDense Blockにつなぐ
音声生成モデル. 時系列データに対して畳み込み(Dilated convolution)を行う.
Dialeted convolution
ミニバッチ単位で平均が0、分散が1になるように正規化する.過学習の抑制を行う. 同一チャネルが正規化の対象となる. RGBの画像であれば、Rの画像をミニバッチ分、Gの画像をミニバッチ分、Bの画像をミニバッチ分集めて正規化を行う
問題点
RGBの画像であれば、1枚の画像のR、G、Bのチャネルをごちゃ混ぜにして正規化を行う.
各チャネルごとに正規化する。RGBの画像であれば、Rの画像だけ、Gの画像だけ、Bの画像をだけを対象にそれぞれ正規化を行う
MoblileNetではdepthwise Convolutionを行っていた. depthwise Convolutionでは、チャネルごとに1つのフィルターがあり、フィルターの大きさは同一だった. MixConvという手法では、チャネルごとに1つのフィルターがあることは変わりはないが、フィルターの大きさを可変にしている. フィルターの大きさはハイパーパラメータだが、depthwise Convolutionよりも同一学習回数の場合、より良い結果が得られた.
フィルターのサイズを決めるパイパーパラメータを決めることにまた頭を悩ませそうだなと感じた.
1400万件以上の写真のデータセットImageNet学習済みモデルの一つにResNetがある. ResNet50, ResNet101, ResNet152のバリエーションがあり、パラメータ数が異なる.
ResNetの構造にSkipConnectionがあり、勾配消失や勾配爆発をしないようにしている.また、Bottleneck構造も使用している.
WideResNet: ResNetにおけるフィルタ数をk倍して、層数を浅くした
import tensorflow as tf
import numpy as np
import tensorflow_datasets as tfds
tf_flowers = tfds.image_classification.TFFlowers()
tf_flowers.download_and_prepare()
# 訓練データ70%,検証データ30%に分ける
(train_ds, test_ds) = tf_flowers.as_dataset(as_supervised=True, split=['train[:70%]', 'train[70%:]'], shuffle_files=True)
fig = tfds.show_examples(train_ds, tf_flowers.info)
# 出力結果
Downloading and preparing dataset tf_flowers/3.0.1 (download: 218.21 MiB, generated: 221.83 MiB, total: 440.05 MiB) to /root/tensorflow_datasets/tf_flowers/3.0.1...
WARNING:absl:Dataset tf_flowers is hosted on GCS. It will automatically be downloaded to your
local data directory. If you'd instead prefer to read directly from our public
GCS bucket (recommended if you're running on GCP), you can instead pass
`try_gcs=True` to `tfds.load` or set `data_dir=gs://tfds-data/datasets`.
Dl Completed...: 100%
5/5 [00:06<00:00, 1.46s/ file]
Dataset tf_flowers downloaded and prepared to /root/tensorflow_datasets/tf_flowers/3.0.1. Subsequent calls will reuse this data.
class_num = tf_flowers.info.features['label'].num_classes
class_num
5
IMAGE_RES = 224 # 揃えたい解像度
def format_image(image, label):
# リサイズと、RGB255で割ることで正規化をおこなっている
image = tf.image.resize(image, (IMAGE_RES, IMAGE_RES)) / 255.0
label = tf.one_hot(label, depth=class_num)
return image, label
BATCH_SIZE = 16
train_batches = train_ds.map(format_image).batch(BATCH_SIZE).prefetch(1)
test_batches = test_ds.map(format_image).batch(BATCH_SIZE).prefetch(1)
print(train_batches)
print(test_batches)
<PrefetchDataset element_spec=(TensorSpec(shape=(None, 224, 224, 3), dtype=tf.float32, name=None), TensorSpec(shape=(None, 5), dtype=tf.float32, name=None))>
<PrefetchDataset element_spec=(TensorSpec(shape=(None, 224, 224, 3), dtype=tf.float32, name=None), TensorSpec(shape=(None, 5), dtype=tf.float32, name=None))>
# resnetのロード
# 事前学習済モデルを使用しない場合
# weights=Noneで重みが初期化されるため、事前学習モデルを使用しないことになる
# resnet = tf.keras.applications.resnet.ResNet50(weights=None)
# resnet.trainable = True
# 事前学習済モデルを使用する場合
# imageNetを利用する場合はweigthsを指定する
resnet = tf.keras.applications.resnet.ResNet50(weights='imagenet')
# trainableをfalseにして重みの値を固定する
#resnet.trainable = False
# ファインチューニングする場合はresnet.trainableはtrueにする
resnet.trainable = True
x1 = resnet.layers[-2].output # avg_poolまでのoutputを取得します。
out = tf.keras.layers.Dense(class_num, activation='softmax')(x1) # avg_poolから出力層に繋げます。
model = tf.keras.models.Model(inputs=resnet.input, outputs=out)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['acc'])
model.summary()
# 出力
==================================================================================================
Total params: 23,597,957
Trainable params: 23,544,837
Non-trainable params: 53,120
__________________________________________________________________________________________________
histories = []
EPOCHS = 20
# EarlyStopping で val_acc: 検証時に正答率が2回以上上がらなかったら学習を中断する設定をしている
history = model.fit(train_batches,
epochs=EPOCHS,
batch_size=BATCH_SIZE,
validation_data=test_batches,
callbacks=[
tf.keras.callbacks.EarlyStopping(monitor='val_acc', patience=2, mode='max')
])
histories.append(history)
ファインチューニングをすると94%の正答率になっていることがわかる.またEarlyStoppingを指定したため正答率が2回上がっていないエポック数で学習を終了している.
AlexNet以降はCNNモデルを大規模にスケールアップする事で精度を改善するアプローチが主流になった.しかしながらモデルが複雑で高コストであることが課題であった.
スケールアップ対象
EfficiecntNetは2019年に発表された. 効率的なスケールアップ規則(複合係数)を採用し、パラメータ数を大幅に減少させた.
EfficiecntNet-B4はResNet50と同程度の処理速度と計算速度で6.3%精度が改善している.また構造がシンプルであり転移学習にも有用である.
代表的なデータセット
物体検出コンペティションで用いられたデータセット.
Confusion Matrix
物体検出はconfidenceの閾値閾値を上げるとConfusion Matrixに数え上げられる数が変わる.
物体検出は、conf.の閾値と、IoU: Intersection over Union の閾値でTPを判定する.
AP: Avarage Precision
クラスラベルを固定してconf.の閾値を変動させて、IoUを固定してPrecision, Recallを算出すると、PR曲線(Precision-Recall curve)が書ける.
APの定義は以下である.PR曲線($P= f(R)$の下側面積となる. $$ \displaystyle AP = ∫_0^1P(R)dR $$ 各クラスで平均を取るものがmean Average Precision $$ \displaystyle mAP = \frac{1}{C} Σ_{i=1}^CAP_i $$
検出速度の指標も大事になる
2012: AlexNetの登場から SIFT -> DCNNへ
特徴マップからの出力
多数のDefault Boxを用意したことで生じる問題
Convolution + Poolingをしていくと入力画像の解像度が落ちていく. でも元画像と同じ解像度にして各ピクセルごとにクラス分類が行われるようにしないといけない. そのため落ちた解像度を元の解像度に戻すこと(Up-sampling)が必要.
処理手順
輪郭情報の補完: 低レイヤーPooling層の出力を要素ごとに足し算することでローカルな情報(≒輪郭)を復元していく
SSD同様に、単一ニューラルネットワークから物体検出を行うモデルにYOLOがある. ただし、YOLOはbinding boxを利用している.
Grid cellの採用が工夫ポイントとなっている. Grid cellの中心座標とするB個のbinding boxを用意する. クラス分類: 各Grid cellごとに含む物体のクラスを分類
候補領域とクラス分類の結果を掛け合わせてクラス分類を行う.
Mask R-CNNはFaster R-CNNの拡張版.
RoI Pooling
RoI Align
完全畳み込みネットワーク(FNC; Fully Convolutional Network)
RCNNは2つのパートで構成されている.
課題として、パート1の処理にSelecitve Searchを採用していて処理速度が遅いことが挙げられる.
物体候補領域の提案の処理にCNNを使用するRPN(Region Proposal Network)を使っていて、パート1パート2一貫した学習が可能になった.
FCOSとはFully Convolutional One-Stage Object Detectionのこと.
既存のモデルでは、バウンディングボックスの候補のアンカーボックスを大量に生成している. また、アンカーボックスはハイパーパラメータ(サイズ・アスペクト比・数)に依存している. アスペクト比比が固定されていると向き・角度・小型の物体に対応できないことがある. アンカーボックスのほとんどがネガティブサンプルで学習がうまくいかなくなることがある.
アンカーボックスを使わない
proposalを使わない.
Feature Pyramid Networks: 複数のサイズの特徴マップを使用する. 高解像度・低解像度の特徴マップを使う
出力
BERT理解までの流れは以下のようになる.
Seq2SeqはEncoder-Decoder Modelであり、系列を入力をとして系列を出力するもの.RNNと言語モデルを理解しておけばよい.
文章の各単語が現れる際の同時確率は、事後確率で分解でき、事後確率を求めることがRNNの目標になる.
注意機構には2種類ありTransformerは自己注意機構を使用している.
BERTでは双方向Transformerを採用
!pip install mecab-python3 # 形態素解析エンジン
!pip install unidic # mecabの辞書として利用
!python -m unidic download
!pip install fugashi # mecabのラッパー
!pip install ipadic # ipa辞書
!pip install transformers
# 夏目漱石のデータのダウンロード
!wget https://www.aozora.gr.jp/cards/000148/files/773_ruby_5968.zip
!unzip -O sjjs /content/773_ruby_5968.zip
!wget https://www.aozora.gr.jp/cards/000148/files/56143_ruby_50824.zip
!unzip -O sjjs /content/56143_ruby_50824.zip
!wget https://www.aozora.gr.jp/cards/000148/files/799_ruby_6024.zip
!unzip -O sjjs 799_ruby_6024.zip
!apt install nkf # network kanji filter
!nkf -w --overwrite kokoro.txt sorekara.txt yume_juya.txt # 古い漢字を変換する
!cat kokoro.txt sorekara.txt yume_juya.txt > train.txt
from transformers import TFBertModel
from transformers import BertJapaneseTokenizer
import os # osをimportしておかないとtokenizerの初期化ができなかった
# BERTの準備
tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
bert = TFBertModel.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking')
import MeCab
import numpy as np
import tensorflow as tf
with open('train.txt', 'r', encoding='utf-8') as f:
text = f.read().replace('\n', '')
mecab = MeCab.Tagger("-Owakati")
text = mecab.parse(text).split()
vocab = sorted(set(text))
char2idx = {u: i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)
text_as_int = np.array([char2idx[c] for c in text])
seq_length = 128
# 訓練用サンプルとターゲットを作る
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)
sequences = char_dataset.batch(seq_length+1, drop_remainder=True)
def split_input_target(chunk):
input_text = chunk[:-1]
target_text = chunk[1:]
return input_text, target_text
dataset = sequences.map(split_input_target)
for input_example, target_example in dataset.take(3):
print(f'Input data: {repr("".join(idx2char[input_example.numpy()]))}')
print(f'Target data: {repr("".join(idx2char[target_example.numpy()]))}')
nput data: 'こころ夏目漱石-------------------------------------------------------【テキスト中に現れる記号について】《》:ルビ(例)私《わたくし》は|:ルビの付く文字列の始まりを特定する記号(例)先生一人|麦藁帽《むぎわらぼう》を[#]:入力者注主に外字の説明や、傍点の位置の指定(数字は、JISX0213の面区点番号、または底本のページと行数)(例)※[#「てへん'
Target data: '夏目漱石-------------------------------------------------------【テキスト中に現れる記号について】《》:ルビ(例)私《わたくし》は|:ルビの付く文字列の始まりを特定する記号(例)先生一人|麦藁帽《むぎわらぼう》を[#]:入力者注主に外字の説明や、傍点の位置の指定(数字は、JISX0213の面区点番号、または底本のページと行数)(例)※[#「てへん+'
Input data: '劣」、第3水準1-84-77]-------------------------------------------------------[#2字下げ]上先生と私[#「上先生と私」は大見出し][#5字下げ]一[#「一」は中見出し]私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その'
Target data: '」、第3水準1-84-77]-------------------------------------------------------[#2字下げ]上先生と私[#「上先生と私」は大見出し][#5字下げ]一[#「一」は中見出し]私《わたくし》はその人を常に先生と呼んでいた。だからここでもただ先生と書くだけで本名は打ち明けない。これは世間を憚《はば》かる遠慮というよりも、その方'
Input data: 'が私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。私が先生と知り合いになったのは鎌倉《かまくら》である。その時私はまだ若々しい書生であった。暑中休暇を利用して海水浴に行った友達からぜひ来いという端書《はがき》を受け取ったので、私は多少の金を工面《くめん》し'
Target data: '私にとって自然だからである。私はその人の記憶を呼び起すごとに、すぐ「先生」といいたくなる。筆を執《と》っても心持は同じ事である。よそよそしい頭文字《かしらもじ》などはとても使う気にならない。私が先生と知り合いになったのは鎌倉《かまくら》である。その時私はまだ若々しい書生であった。暑中休暇を利用して海水浴に行った友達からぜひ来いという端書《はがき》を受け取ったので、私は多少の金を工面《くめん》して'
BATCH_SIZE = 64
BUFFER_SIZE = 10000
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
input_ids = tf.keras.layers.Input(shape=(None, ), dtype='int32', name='input_ids')
inputs = [input_ids]
bert.trainable = False # ファインチューニングはしない
x = bert(inputs)
out = x[0]
Y = tf.keras.layers.Dense(len(vocab))(out)
checkpoint_dir = './training_checkpoints'
checkpoint_prefix = os.path.join(checkpoint_dir, "ckpt_{epoch}")
checkpoint_callback=tf.keras.callbacks.ModelCheckpoint(
filepath=checkpoint_prefix,
save_weights_only=True)
model = tf.keras.Model(inputs=inputs, outputs=Y)
def loss(labels, logits):
return tf.keras.losses.sparse_categorical_crossentropy(labels, logits, from_logits=True)
model.compile(loss=loss,
optimizer=tf.keras.optimizers.Adam(1e-7))
model.fit(dataset,epochs=5, callbacks=[checkpoint_callback])
def generate_text(model, start_string):
# 評価ステップ(学習済みモデルを使ったテキスト生成)
# 生成する文字数
num_generate = 30
# 開始文字列を数値に変換(ベクトル化)
input_eval = [char2idx[s] for s in start_string]
input_eval = tf.expand_dims(input_eval, 0)
# 結果を保存する空文字列
text_generated = []
temperature = 1
# ここではバッチサイズ == 1
model.reset_states()
for i in range(num_generate):
predictions = model(input_eval)
# バッチの次元を削除
predictions = tf.squeeze(predictions, 0)
# カテゴリー分布をつかってモデルから返された言葉を予測
predictions = predictions / temperature
predicted_id = tf.random.categorical(predictions, num_samples=1)[-1,0].numpy()
# 過去の隠れ状態とともに予測された言葉をモデルへのつぎの入力として渡す
input_eval = tf.expand_dims([predicted_id], 0)
text_generated.append(idx2char[predicted_id])
return (''.join(start_string) + ''.join(text_generated))
text = '私は'
mecab = MeCab.Tagger("-Owakati")
text = mecab.parse(text).split()
generate_text(model, text)
# 出力結果
私は少時茶壺彫近所揺れる下っ動ける行き怒らすまくらべ搗下界長屋落し待っ片付ける同じくろう逃口放蕩両国一理うぎょ無益新旧きら約家族つづい突き刺す
出力の「私は」に続く文字がめちゃめちゃになっている。今回の学習は教師データとして、夏目漱石のデータセットの文字列をソートし、文章の順序を意味のない順序に変えたからと考えられる.
BERTはどこで使われているのか知るために活用例を調べてみた.
Googleが研究中の新AI披露、自然な対話や意図をくみ取る検索: https://xtech.nikkei.com/atcl/nxt/column/18/01537/00082/?P=2
GPT(Generative Pre-Training)
社会の安全に関する課題
音声データを処理するAIの活用事例
波形処理
いろいろな処理
CTCはConnectionist Temporal Classificationの略.
CTCは音響モデル・発音モデル・言語モデルを1つのDNNで表現するEnd-to-Endモデルの1つになっている.従来手法のように隠れマルコフモデルを使用するのではなく、DNNだけで音響モデルを構築する手法として提案された.以下の2つが重要な要素になっている.
音声の入力フレームごとに音素の確率を出力する. 該当する音素がないと判断した時に、ブランクとして該当する要素がないという情報を出力するようにしている.そうすることで以下のメリットがある.
あるフレームtでどの拡張ラベルs(各ラベルの前後にブランクを挿入したもの)を通るかを考えると、その前側(その頂点に到達するまでの全パスの確率の総和; 前向き確率)の確率と後側(その頂点から終点まで到達する全パスの総和; 後ろ向き確率)の確率を出すことができる.
CTCの損失関数を前向き確率と後ろ向き確率で表せることができ、学習をしていくことが可能となる.
GANはGenerative Adversarial Netsの略. 生成器Gと識別器Dを競わせて学習する生成&識別モデル.で2プレイヤーのミニマックスゲームになっている.
GANでは価値関数V(バイナリークロスエントロピー)に対して、Dが最大化、Gが最小化を行う. $$ \underset{G}{\text{min}} \ \underset{D}{\text{max}} V(D, G) $$
DCGANはDeep Convolutional GANの略. GANからいくつかの構造制約により生成品質を向上した.
Fast Bi-layer Neural Synthesis of One-Shot Realistic Head Avatars(E. Zakharov. et al., 2020)
静止画の顔画像から動くアバターを生成することができる. ぼやけた動画と輪郭情報を掛け合わせることではっきりした動画を生成している.
モデル構造
Texture GeneratorとInference Generatorの出力を計算し所望のポーズで動くアバターを最終出力とする.
DCGANクラスのdiscriminatorとgeneratorの更新について確認する.
class DCGAN(object):
# (略)
@tf.function
def update_discriminator(self, noize, real_data):
fake_data = self.G(noize) # <- Generatorを使って生成データを作成する
with tf.GradientTape() as d_tape:
real_pred = self.D(real_data)
fake_pred = self.D(fake_data)
# 真データの損失を計算している: ラベル1
real_loss = tf.keras.losses.binary_crossentropy(
tf.ones_like(real_pred), real_pred
)
# 生成データの損失を計算している: ラベル0
fake_loss = tf.keras.losses.binary_crossentropy(
tf.zeros_like(fake_pred), fake_pred
)
# batchの平均をとる
real_loss = tf.math.reduce_mean(real_loss)
fake_loss = tf.math.reduce_mean(fake_loss)
adv_loss = real_loss + fake_loss
# 勾配の計算
d_grad = d_tape.gradient(adv_loss, sources=self.D.trainable_variables)
# Descripminatorのパラメータをd_gradで更新する
self.d_optimizer.apply_gradients(zip(d_grad, self.D.trainable_variables))
if self.make_logs:
with self.summary_writer.as_default():
tf.summary.scalar("d_loss", adv_loss)
self.summary_writer.flush()
return adv_loss
@tf.function
def update_generator(self, noize):
with tf.GradientTape() as g_tape:
fake_data = self.G(noize) # <- Generatorを使って生成データを作成する
fake_pred = self.D(fake_data) # <- Descriminatorで生成データを予測する
# max(log(D(x))) こちらの方が勾配消失に頑健
# ラベル1でfake_predを扱っているところがあまりわかっていないが後日再考する.
fake_loss = tf.keras.losses.binary_crossentropy(
tf.ones_like(fake_pred), fake_pred
)
# min(1-log(D(x)))
# fake_loss = -tf.keras.losses.binary_crossentropy(
# tf.zeros_like(fake_pred), fake_pred
# )
# batchの平均をとる
fake_loss = tf.math.reduce_mean(fake_loss)
g_grad = g_tape.gradient(fake_loss, sources=self.G.trainable_variables)
# Generatorのパラメータをg_gradで更新する
self.g_optimizer.apply_gradients(zip(g_grad, self.G.trainable_variables))
if self.make_logs:
with self.summary_writer.as_default():
tf.summary.scalar("g_loss", fake_loss)
self.global_step.assign_add(1)
return fake_loss
条件付き敵対的生成ネットワーク
画像生成時に条件パラメータを与えることで、生成したい画像のクラスを指定できる.従来のGANでは生成したいクラスの指定はできない.
Generator
Descripminator
3つの工夫
A3CとはAsynchronous Advantage Actor-Criticの略.
Actor-Criticとは、Actorを改善しながら方策を評価するCriticを同時に学習させるアプローチのこと.
A3Cのメリット
Atari2600における深層強化学習ではA3Cはより短い訓練時間でGPUなしでも他の手法を上回るスコアを出していた.
距離学習は人物同定、顔認識、画像分類、画像検索、異常検知などさまざまなタスクで利用されている技術.
入力の畳み込み処理後に生成される特徴ベクトルを埋め込み空間上で、類似のものは近くに配置するように学習ができれば分類が容易となる.
類似度を反映した埋め込み空間を構成できるように学習を行うだけで精度向上が可能になるというのが、深層学習のアプローチになる.
2006年に提案された手法. 2つのサンプルをペアで入力してそれらのサンプル間の距離を明示的に表現して調整している.
損失関数(copntrastive loss)Lは以下のように表せる.
$$ \displaystyle L = yL1 + (1-y)L2 = \frac{1}{2} \left[yD^2 + (1-y)\ max(m-D, 0)^2 \ \right] $$
この損失関数には問題があり、入力ペアが異なるクラスの場合は、mを超えたときに学習を修了するのに対して、同一クラスの場合はD=0になるまで学習を続けてしまい、良い埋め込み空間が得られなくなっていた.
2014年に提案された手法でSiamese Networkの課題を解決するために考案された. 3つのサンプルをセットにして入力する.3つのサンプルは、類似サンプル、アンカーサンプル、非類似サンプルとする.
損失関数は以下のように表せる.
$$ \displaystyle L = max(D_p - D_n + m, 0) $$
アンカーサンプルに類似サンプルが近く、非類似サンプルが類似サンプルよりもマージン以上遠くなるように設計されている.
Triplet networkの問題点
MAMLが解決したい課題
データセットの画像枚数
MAMLのコンセプト
これまで事前学習を行い、転移学習やファインチューニングをおこなってきた. MAMLではタスクごとにファインチューニングを行い、タスク共通重みを更新し、さらに新しいタスクをファインチューニングを行う、ということを繰り返し、さまざまなタスクの共通な重みを更新してタスクへの汎用性を持たせていく.
MAMLの課題と対処
CNNの復習
CNNの弱点
GCNのポイント3つ
Spatial GCNとSpectral GCNが主流
GCNの弱点
GCNの計算 $$ u’ = QΘQ^⊤u $$
解釈性の重要性
モデルの解釈に使われる手法
CNNモデルに判断根拠を持たせてモデルの予測可能性を可視化する方法.
GAP(Global Average Pooling)がCNNが潜在的に注目している部分を可視化できるようにする役割を持っていそうなことがわかった.
CAM同様、CNNモデルに判断根拠を持たせてモデルの予測可能性を可視化する方法.
GradはGradient(勾配)が由来.最後の畳み込み層の予測クラスの出力値に対する勾配を利用しており、勾配が大きいピクセルに重みを増やすことで予測クラスの出力に大きく影響する重要な場所であると判断している.
Grad-CAMとCAMの違い
協力ゲーム理論の概念であるshapley valueを機械学習に応用した.
shapley value: プレイヤーが協力しそれによって獲得した報酬を貢献度をもとに分配した値. 平均的な限界貢献度のことを指す.
import os
ENV_COLAB = True if 'google.colab' in sys.modules else False
if ENV_COLAB:
from google.colab import drive
drive.mount('/content/drive')
os.chdir('/content/drive/My Drive/rabitchallenge/Stage4/DNN_code_colab_day4/notebook')
import numpy as np
import cv2
import tensorflow as tf
from tensorflow.keras.applications import VGG16 # 学習済のVGG16モデルを利用する
class GradCam:
def __init__(self, model):
self.model = model
# 畳み込み最終層の名前を確認するため
print([layer.name for layer in self.model.layers])
def gradcam_func(self, x, layer_name):
# 一枚の画像だと、バッチの次元がないので足す
X = x[np.newaxis, ...]
# 正規化
X = X / 255.
# 畳み込み層の最後の層の出力を受け取る
conv_feature = self.model.get_layer(layer_name).output
# ## 特徴マップを受け取れるようにするために入力と出力を指定し直す
model = tf.keras.Model([self.model.inputs], [conv_feature, self.model.output])
# 勾配を記録するために tf.GradientTape() を使う
with tf.GradientTape() as tape:
# numpy配列を勾配を計算するためにtfの型に変換する
X = tf.cast(X, tf.float32)
conv_feature, outputs = model(X)
# どのクラスを予測したか
predicted_class = tf.math.argmax(outputs[0])
# 予測したクラスの出力を取得する
class_outputs = outputs[:, predicted_class]
# 勾配を計算する
grads = tape.gradient(class_outputs, conv_feature)
print('予測クラス', predicted_class.numpy())
# 平均を取る(GAP: gloval avarage pooling)
weights = tf.math.reduce_mean(grads, axis=(1, 2))
cam = conv_feature @ weights[..., tf.newaxis] # 線型結合している
cam = tf.squeeze(cam)
# reluに通す
cam = tf.nn.relu(cam)
# 0に近い値がreluを通すと出力されるので値を大きくする
cam = cam / tf.math.reduce_max(cam)
# 正規化を戻す
cam = 255. * cam
# numpy配列にする
cam = cam.numpy()
cam = cam.astype('uint8')
# カラーマップを作る
jetcam = cv2.applyColorMap(cam, cv2.COLORMAP_JET)
# BGRからRGBに変換
jetcam = cv2.cvtColor(jetcam, cv2.COLOR_BGR2RGB)
jetcam = cv2.resize(jetcam, (224, 224))
jetcam = jetcam + x / 2
return jetcam
model = VGG16(weights='imagenet') # VGG
gradcam = GradCam(model)
image = cv2.imread('../data/interpretability_example_input.png')
image = cv2.resize(image, (224, 224))
cam = gradcam.gradcam_func(image, 'block5_conv3') #畳み込み層の最終層の名前を引数に取る
from google.colab.patches import cv2_imshow
cv2_imshow(image)
cv2_imshow(cam)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5
553467904/553467096 [==============================] - 3s 0us/step
553476096/553467096 [==============================] - 3s 0us/step
['input_1', 'block1_conv1', 'block1_conv2', 'block1_pool', 'block2_conv1', 'block2_conv2', 'block2_pool', 'block3_conv1', 'block3_conv2', 'block3_conv3', 'block3_pool', 'block4_conv1', 'block4_conv2', 'block4_conv3', 'block4_pool', 'block5_conv1', 'block5_conv2', 'block5_conv3', 'block5_pool', 'flatten', 'fc1', 'fc2', 'predictions']
予測クラス 999
トイレットペーパーと判断したするために重視した範囲をヒートマップ表示している. ヒートマップを見るとトイレットペーパーそのものを判断しているというより、トイレットペーパーの黒い背景の周辺部分で判断していることを示している.
コンテナは軽量で効率化の度合いを高めることができる.
Dockerはコンテナ型の仮想化を提供する. カーネル、ネットワーク、ファイルシステムをホストOSで実行されるユーザープロセスとして共有するため、論理的隔離の度合いは低いため、カーネルへの攻撃を考えるとセキュリティ強化が必要.
docker-compose.yamlにサービスの構成やサービスの依存関係を記載することでdockerコマンドのオペレーションを整理することができる.
Amazon web servicesではコンテナーのオーケストレーションツールであるkubernetesをマネージドサービスとして提供する Amazon Elastic kubernetes service(EKS)がある. Microsoft Azureには同等のサービスAKSがある.
AWS Fargeteを使うと、kubernetesのインフラ基盤のEC2の管理などが不要になり、コンテナーやアプリの管理に集中できる.
機械学習フレームワークがプリインストールされたDockerイメージである、AWS Deep Lerning Containersを使えば、自分でライブラリを準備が手間が省ける. (参照: https://aws.amazon.com/jp/machine-learning/containers/)