アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

自然言語処理タクスでよく使うAttentionの出力のAttention weightを可視化してみた【機械学習】

Attentionといえば、すでに自然言語処理モデルではなくてはならない存在。
カニズムは割愛。別名で注意機構とか呼ばれる。

Attentionの仕組みは、(個人的に理解してる範囲では)簡単に言うと以下のポイントがある。

・人間が特定のことに集中(注意)する仕組みと同じ

・Attentionの仕組みはAttention自体が特定の単語に注意(注目)する

・Attentionの挙動は人間の直感に近い

今回はそのAttentionが「どの単語を注意して見てるのか」わかるように、Attentionの出力結果Attention weightを可視化してみた。


こんな感じ
f:id:trafalbad:20190804133028j:plain


その過程を備忘録も兼ねてまとめてく。

目次
・今回の記事の概略
1.データ読み込み
2.モデル構築・訓練
3.Attention可視化用に訓練したモデルを再構築
4.Attention weightの可視化
・まとめ




今回の記事の概略

タスクとデータセットは、前回の日本語版BERTの記事で使った" livedoorニュースコーパス "を使ったトピック分類で、「Sports、トピックニュース」のトピックを分類するタスク。

BERTでのAttention可視化は

・BERTではマスク処理がある

・MaltiHeadAttentionを使ってる

等の理由で基本的にBERTでのAttentionの可視化はできないっぽいので、簡易モデルを作ってAttentionがどの単語に注意を払ってるのか可視化してみた。


AttentionにはMaltiHeadAttentionとか、いろいろ種類があるが、可視化にはselfAttentionが使われる。
f:id:trafalbad:20190804133614j:plain




selfAttentionを含めてAttentionの仕組みは下記サイトに詳しく載ってる。
qiita.com


幸い、kerasにpipでinstallできるselfAttentionがあるので、それを使ってAttentionの出力のAttention weightを可視化してみる。





1.データ読み込み

前回記事の日本語版BERTで使用した
・前処理済みテキストデータ

・idベクトル化してない日本語のテキストデータ(all_txet.npy)

を使う。

# 読み込み
train_x = np.load('train_xs.npy')
train_y = np.load('train_label.npy')
test_x = np.load('test_xs.npy')
test_y = np.load('test_label.npy')
# id化してない日本語の文章も読み込み
all_text = np.load('all_text.npy')

# one-hot表現
n_labels = len(np.unique(train_y))
train_y=np.eye(n_labels)[train_y] 
train_y = np.array(train_y)


2.モデル構築・訓練

BERTは上述の通り、マスク処理とMaltiHeadAttentionを使ってるので、Attentionの可視化はできなかった。

なのでAttention可視化用に、双方向LSTMを使った簡易モデルを作成して、学習した

h_dim=356
seq_len = 691
vocab_size = 23569+1

inp = Input(batch_shape = [None, seq_len])
emb = Embedding(vocab_size, 300)(inp) # (?,128,32)
att_layer = SeqSelfAttention(name='attention')(emb)  # embbedingレイヤーの後にselfattentionを配置
out = Bidirectional(LSTM(h_dim))(att_layer)
output = Dense(2, activation='softmax')(out)  # shape=(?, 2)
model = Model(inp, output)
model.compile(optimizer='Adam', loss='categorical_crossentropy', metrics=['acc', 'mse', 'mae'])
model.summary()
>>>
==================================================
input_1 (InputLayer) (None, 691) 0 _________________________________________________________________ embedding_1 (Embedding) (None, 691, 300) 7071000 _________________________________________________________________ attention (SeqSelfAttention) (None, 691, 300) 19265 _________________________________________________________________ bidirectional_1 (Bidirection (None, 712) 1871136 _________________________________________________________________ dense_1 (Dense) (None, 2) 1426 ======================================================



# train
model.fit(train_x, train_y, epochs=1, batch_size=10)

# 予測
predicts = model.predict(test_x, verbose=True).argmax(axis=-1)
print(np.sum(test_y == predicts) / test_y.shape[0])

正解率は、前処理とAttentionのおかげで、BERT並みの92%





3.Attention可視化用に訓練したモデルを再構築

訓練したモデルでAttention weight可視化用にモデルの再構築。

selfAttentionのレイヤーの出力

最後の出力層の出力

の両方をModelのoutputに追加

emodel = Model(inputs=model.input, outputs=[model.output, model.layers[2].output])
emodel.summary()

>>>
======================================================
 input_2 (InputLayer) (None, 691) 0 _________________________________________________________________ embedding_2 (Embedding) (None, 691, 300) 7071000 _________________________________________________________________ attention (SeqSelfAttention) (None, 691, 300) 19265 _________________________________________________________________ bidirectional_2 (Bidirection (None, 712) 1871136 _________________________________________________________________ dense_2 (Dense) (None, 2) 1426 ====================================================

精度も申し分ないので、後はどこの単語をAttentionが注目してるのかを可視化するだけ。




4.Attention weightの可視化

まずkeras のselfAttentionをこのサイトからインストール。

$ pip install keras-self-attention


Attention weightを可視化


どの単語に注目してるかの重みの総和を計算。


from keras_self_attention import SeqSelfAttention
import pandas as pd

# 予測後、Attentionの出力((batch, words, 300)=(batch, 691, 300))を取り出す
predict=emodel.predict(test_x)

token = all_text[700]

# 対象の文章(1batch)の中の175個のwords一つ一つから、3次元目(300dim)のmax値とる  =>shape=(1, 175, 1)
weight = [w.max() for w in predict[1][0][:175]]  # test_x[0][:176]

# pandasに入れてまず数値化。そのあとHTML形式にして、jupyter上で可視化
df = pd.DataFrame([token, weight], index=['token', 'weight'])
mean = np.array(weight).mean()
print(df.shape, mean)
df = df.T

df['rank'] = df['weight'].rank(ascending=False)

# 各wordsのmax値から全max値の平均を引き Attention  weightを計算。マイナスの値は0扱い
df['normalized'] = df['weight'].apply(lambda w: max(w - mean, 0))
df['weight'] = df['weight'].astype('float32')
df['attention'] = df['normalized'] > 0



【pandasの可視化結果】
f:id:trafalbad:20190804133443j:plain

ちなみにHTML形式でjupyter上で可視化するときは下のメソッドを使った。

from IPython.core.display import display, HTML
# HTMLで可視化
display(HTML(html))


【トピックを「Sports」と予測できたときのattention weight & その文章の可視化結果】
f:id:trafalbad:20190804133028j:plain




データセットが少なく、語彙数が少ないのもあるが、「ドラフト、日本ハム、指名、会議」とか、スポーツ(野球)に関連しそうなワードが赤いので、そこにAttentionが注目してるのがわかる。

噂通り、割と人間の直感に近い感じの語彙に注目してる。





Attention weight可視化で気づいたことメモ



①可視化するselfattentionレイヤーの出力はshapeは3次元でもいい


②selfattentionレイヤーはembbedingレイヤーの後に配置するのが定石っぽい


③BERTでのAttention可視化は無理(っぽい)
→マスク処理してる

→MaltiHeadAttentionを使ってる

→工夫次第ではできそう



④matplotlibで可視化もできる。
f:id:trafalbad:20190804133632j:plain


まとめ

はじめはAttentionを書籍で読んだり、調べたりしてもサッパリだった。

けど、実務で使って考えまくることで、仕組み・種類、使い方、なんで精度高くなるのとかかなり理解できた。

経験に勝る知識なしっていう格言のいい経験。



参考サイト
selfattentionを簡単に予測理由を可視化できる文書分類モデルを実装する

自然言語処理で使われるAttentionのWeightを可視化する

日本語版keras BERTのfine tuningでlivedoorニュースのトピック分類

今回は日本語版keras BERTで、自然言語処理用の公開データセット" livedoorニュースコーパス "のトピック分類をしてみた。

前回の記事で、英語版のkeras BERTでネガポジ判定をしたが、日本語版はやったことなかった。
テキストの前処理→日本語版keras BERT読み込み→訓練までのやった過程とそのメインポイントをまとめてく。


目次
・日本語テキストの前処理
1.テキストデータ読み込み(and クリーニング)
2.単語の正規化
3.mecabで単語の分割
4.stopwordsの除去& 頻度の低い単語除去
5.単語を分散表現でidベクトル化

・日本語版keras BERTで学習
1.BERTの読み込み
2.整形済みテキストの読み込み
3.訓練と正解率



日本語テキストの前処理
今回は公開データセット" livedoorニュースコーパス "のテキストを、トピック別に分類するタスク。


今回は、トピックの「Sports、トピックニュース」をBERTを使い、どのくらいの精度で分類できるかの2値分類を試した。

・訓練データ 1300

・テストデータ 360

BERTのネットワーク構造
f:id:trafalbad:20190803082446j:plain



1.テキストデータ読み込み(and クリーニング)

livedoorのテキストデータは多いので、google driveに保存してから、google colaboratoryとマウントして読み込んだ。

読み込む処理は行ごとにリストに入れて、list[2:]とかのスライス表記で、1行ずつ削除できるよう工夫。

htmlタグとかはないので、特にhtmlパースとかはなし。

c=0
L={}
# テキストののN行目を削除できるよう読み込み
text_path =  ["drive/My Drive/sports-watch", "drive/My Drive/topic-news"]
for i, path in enumerate(os.listdir(text_path[c]), len(L)):
  _list =[]
  with open(text_path[c]+'/'+path, "r") as f:
  #text_list = text_data.readlines()
     for _l in f:
        _l = _l.rstrip()
        if _l:
           _list.append(_l)
  L[i] = _list
c+=1


2.単語の正規化

大体やったことは、

・単語の統一 (半角から全角へ変換)

import mojimoji

S=[]
for i in range(0, len(L)):
  # 半角=>全角
  sen = mojimoji.han_to_zen(sen) 
  S.append(sen)

・数字の置き換え (連続した数字を0に置換)

def normalize_number(text):
    # 連続した数字を0で置換
    replaced_text = re.sub(r'\d+', '0', text)
    return replaced_text

単語の統一はタスクによりけりなので、特にしなかった。





3.mecabで単語の分割

名詞、形容詞、動詞のみ使用するという、よくある精度を上げるテク。

def mecab(document):
  mecab = MeCab.Tagger("-Ochasen")
  lines = mecab.parse(document).splitlines()
  words = []
  for line in lines:
      chunks = line.split('\t')
      if len(chunks) > 3 and (chunks[3].startswith('動詞') or chunks[3].startswith('形容詞') or (chunks[3].startswith('名詞') and not chunks[3].startswith('名詞-数'))):
          words.append(chunks[0])
  return words

4.stopwordsの除去& 頻度の低い単語除去




stopwordsの除去



def delete_stopwords(doc):
  sentence_words = []
  for word in doc:
    # ストップワードに含まれるものは除外
    if word in stopwords: 
      continue
    #  特定の文字列(chr(92)=="\")を含む文字列削除
    if chr(92) in word or '″' in word:    
      continue
    sentence_words.append(word)        
  return sentence_words




頻度の低い単語の除去


tf-idfはクラスタリングで分類して、外れ値的なテキストそのものを削除しまう。そうするとデータセット数が減るのでやめた。

他にも、collectionメソッドで、頻度の少ないwordsのリスト(few_wordlist)を作って、頻度が一回以下の単語を削除してみたけど、文章がほとんど原型を留めてなかったのでそれもNG。

結果的に、頻度の低い単語は削除しせずとも、精度は高かった。(多分、attentionのおかげ)。頻度の低い単語を削除するのは、適材適所で使った方がいい。


使うと逆に精度を下げることがあるので注意。



・tf-idfで削除

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
 
vectorizer = TfidfVectorizer(use_idf=True, token_pattern=u'(?u)\\b\\w+\\b')
vecs = vectorizer.fit_transform(docs)

# クラスタリング
clusters = KMeans(n_clusters=5, random_state=0).fit_predict(vecs)
docs = [doc for doc in docs]

参考サイト:TF-IDF で文書をベクトル化。python の TfidfVectorizer を使ってみる



・頻度の低い単語の削除

import collections

def delete_sewwords(doc):
  sentence_words = []
  for word in doc:
# few_wordlist 含まれるものは除外
    if word in few_wordlist:
      continue
    sentence_words.append(word)        
  return sentence_words


#  few_wordslistを作る
count = collections.Counter(docs)
few_wordlist=[]
for word, amount in count.items():
  if amount >1:
    continue
  few_wordlist.append(word)

# 頻度の少ない単語を除外
train_x=[]
for doc in docs:
  doc = delete_sewwords(doc)
  train_x.append(doc)

5.単語を分散表現でidベクトル化

sentencepieceを使って、2次元のidベクトル化する例が多くなってるけど、日本語だと上手くいってないケースが散見されるので、今回は純粋に正攻法でidベクトル化した。

# 単語のidx辞書
words = {}
for sentence in SS:
  for word in sentence:
    if word not in words:
#  '+1' がゼロパディングのキーポイント
      words[word] = len(words)+1


# 文章を単語ID配列にする
data_x_vec = []
for sentence in SS:
  sentence_ids = []
  for word in sentence:
    sentence_ids.append(words[word])
  data_x_vec.append(sentence_ids)

# 文章の長さを揃えるため、0でパディングする
max_sentence_size = 0
for sentence_vec in data_x_vec:
    if max_sentence_size < len(sentence_vec):
        max_sentence_size = len(sentence_vec)
print(max_sentence_size)   # ==len(sentence_ids)

for sentence_ids in data_x_vec:
    while len(sentence_ids) < max_sentence_size:
         # 末尾に0を追加
        sentence_ids.append(0)

# arrayに変換
data_x_vecs = np.array(data_x_vec, dtype="int32")

# 保存
np.save('train_label', train_sy)
np.save('test_label', test_sy)
# テキストも保存
np.save('all_text', np.array(docs))


日本語版keras BERTで学習

日本語版のkeras bert は下のサイトからに関連ファイルをダウンロード。
www.inoue-kobo.com



BERTの内部構造
f:id:trafalbad:20190803082605j:plain

1.BERTの読み込み

BERT関連ファイルは容量がでかいので、google driveにアップロードして、google colaboratoryとマウントして読み込んだ。

# tensorflowのバージョンを1.13.1に変更
!pip uninstall tensorflow && pip install tensorflow==1.13.1

# colabratory上でgoogle driveとマウント
from google.colab import drive
drive.mount('/content/drive')


keras BERTでは、ネットワーク構造の関係でinput sizeは512がmaxらしいので、bert_config.jsonの"max_position_embeddings"と
"max_seq_length"の値をそれに合わせた。

bert_config.json

{ "attention_probs_dropout_prob": 0.1,
    "hidden_act": "gelu",
    "hidden_dropout_prob": 0.1,
    "hidden_size": 768,
    "initializer_range": 0.02,
    "intermediate_size": 3072,
    "max_position_embeddings": 512,
    "max_seq_length": 512,
    "num_attention_heads": 12,
    "num_hidden_layers": 12,
    "type_vocab_size": 2,
    "vocab_size": 32000}

もともとinput size=(batch, 619)だった(619はindexのmax(seq_lenth)、つまりidのmax)。

なので、各テキストを512に削って、input size=(batch, 512)に整形

train_x = [text[:512] for text in train_x]
train_y = train_y[:512]


あとは前回同様、BERTを読み込んで、分類問題用に、モデルを作り変える。

# BERTの読み込み
from keras_bert import AdamWarmup, calc_train_steps
from keras_bert import get_custom_objects
from keras_bert import load_trained_model_from_checkpoint

pretrained_path = "drive/My Drive/bert-wiki-ja"
config_path = 'bert_config.json'
checkpoint_path = os.path.join(pretrained_path, 'model.ckpt-1400000')

bert = load_trained_model_from_checkpoint(config_path,
    checkpoint_path, training=True, trainable=True, seq_len=SEQ_LEN)
bert.summary()


# 分類問題用にモデルの再構築
inputs = bert.inputs[:2]
dense = bert.get_layer('NSP-Dense').output
outputs = keras.layers.Dense(units=2, activation='softmax')(dense)

decay_steps, warmup_steps = calc_train_steps(train_y.shape[0],
    batch_size=BATCH_SIZE, epochs=EPOCHS)

model = keras.models.Model(inputs, outputs)
model.compile(AdamWarmup(decay_steps=decay_steps, warmup_steps=warmup_steps, lr=LR),
    loss='sparse_categorical_crossentropy', metrics=['sparse_categorical_accuracy'])
model.summary()




2.整形済みテキストの読み込み

訓練用とテスト用のテキストとラベルを読み込む。

BERTではマスク処理をするので、テキストをそれに合わせて整形。

# 読み込み
train_x = np.load('train_xs.npy')

# BERTに合わせて整形
train_x = [train_x, np.zeros_like(train_x)]
test_x= [test_x, np.zeros_like(test_x)]



3.訓練と正解率

sess = K.get_session()
uninitialized_variables = set([i.decode('ascii') for i in sess.run(tf.report_uninitialized_variables())])
init_op = tf.variables_initializer([v for v in tf.global_variables() if v.name.split(':')[0] in uninitialized_variables])
sess.run(init_op)

#  TPU Modelに変換
tpu_address = 'grpc://' + os.environ['COLAB_TPU_ADDR']
strategy = tf.contrib.tpu.TPUDistributionStrategy(tf.contrib.cluster_resolver.TPUClusterResolver(tpu=tpu_address))

with tf.keras.utils.custom_object_scope(get_custom_objects()):
    tpu_model = tf.contrib.tpu.keras_to_tpu_model(model, strategy=strategy)

# train
with tf.keras.utils.custom_object_scope(get_custom_objects()):
    tpu_model.fit(train_x, train_y, epochs=EPOCHS, batch_size=BATCH_SIZE)

# 予測
with tf.keras.utils.custom_object_scope(get_custom_objects()):
    predicts = tpu_model.predict(test_x, verbose=True).argmax(axis=-1)
print(np.sum(test_y == predicts) / test_y.shape[0]) 

前処理をしっかりやったおかげでもあるけど、精度はわずか2エポックで94%という驚異的な精度。
さすがBERT。



まとめ

テキストが日本語の場合、テキスト前処理をsentencepieceとかでお手軽にやってる例があるけど、やっぱりきちんと特徴量エンジニアリング的にしっかり前処理した方が、精度がかなり上がるね。


参考サイト

Keras BERTでファインチューニングしてみる

sentencepieceチュートリアル

学習済み英語版keras BERTのfine tuning(転移学習)でネガポジ判定の2値分類をしてみた【機械学習・自然言語処理】

google自然言語処理の高性能モデルBERTを使ってfine tuning(転移学習)をやった。BERT用のデータセットではなく、一般に公開されてるIMDBデータセット(映画レビュー)を使用。
2値分類用にBERTモデルを再構築して、ネガポジ判定したので、その過程をまてめてく。

目次
・今回のタスク
・データセットの作成と中身
・学習済みBERTのload & 2値分類用に再構築
・正解率
・まとめ & BERTの全体像


今回のタスク

タスクは2値分類のネガポジ判定。データセットIMDBデータセットで中身は「映画のレビューとラベル(negative=0, positive=1)」。

BERTには事前学習と転移学習の二つの使い道があり、普通は事前学習(一から学習)はせず、転移学習で十分高性能が出せるし、

word2vecの拡張でAttentionで文脈を考慮したネガポジ判定ができ、注目した部分をヒートマップで染めることもできる。
f:id:trafalbad:20190720080557j:plain


今回はチュートリアルのこのサイトをほとんど真似させてもらったので、コードはサイトを参照してほしい。Git cloneしたkeras bertを使って、転移学習をやった。

colabratoryでtensorflowのversionが2019/6月から1.14になってて、ネットに出回ってるTPUの使用方法が使えなくなってた。

このサイト通りにやるのもいいけど、keras bertのように、modelを再構築する時にはよくわからないエラーがでまくったので、tensorflowをversion==1.13.1に戻した。

!pip uninstall tensorflow && pip install tensorflow==1.13.1


データセットの作成と中身

データセットの呼び出しはサイト通りなので、その中身をまとめていく。

1 def load_data(path):
2     global tokenizer
3     indices, sentiments = [], []
4     for folder, sentiment in (('neg', 0), ('pos', 1)):
5         folder = os.path.join(path, folder)
6         for name in tqdm(os.listdir(folder)):
7             with open(os.path.join(folder, name), 'r') as reader:
8                   text = reader.read()
9             ids, segments = tokenizer.encode(text, max_len=SEQ_LEN)
10            indices.append(ids)
11            sentiments.append(sentiment)
12     items = list(zip(indices, sentiments))
13     np.random.shuffle(items)
14     indices, sentiments = zip(*items)
15     indices = np.array(indices)
16     return [indices, np.zeros_like(indices)], np.array(sentiments)

データセットの中身



・4行目folder
'/root/.keras/datasets/aclImdb/train/neg'、 ‘/root/.keras/datasets/aclImdb/train/pos'
で文字列の’neg’と’pos’を受け取る。

・4行目のsentiment
[0,1] 。for文から返る右側の0, 1を受け取る

・6行目のname
中身が
['12398_2.txt', '12239_1.txt', '8140_2.txt', ‘9293_1.txt',~]
のtextファイル25000このリスト

・8行目のtext中身(の一部)

Look, it\'s the third one, so you already know it\'s bad. And "Maniac Cop" wasn\'t good enough to warrant the second installment, so you know it\'s even worse. But how much worse? Awful, approaching God-awful.<br /><br />When Maniac Cop goes on a killing spree, a reporter exclaims, "What happened here can ONLY be described as a black rainbow of death."<br /><br />1-- Rainbows are not black, and can never be. 2-- Rainbows are harmless, and can never inflict pain or death. 3-- A news reporter, one valuable to his agency, might find another way to describe the aftermath of a killing spree. "A black rainbow of death" is not the ONLY way to describe the given situation.<br /><br />This is what you\'re in for.’~

・10行目のindices
長さ128のlistが25000こ。len(indices[0])=>128, len(indices)=>25000
[101, 2298,1010, 2009, 1005, 1055, 29310,~,102]

・11行目のsentiments
中身が0, 1のlist、len(sentiments)=>25000
[0,1,0,0,0,1,0,~]

・15行目のindices = np.array(indices)
shape=(25000, 128)のarray。中身は10行目と同じ。

・16行目のnp.array(sentiments)
shape=(25000,)のarray。中身は11行目と同じ

・16行目の[indices, np.zeros_like(indices)]
np.zeros_like()は np.zeros(indices.shape)=np.zeros_like(indices)
中身は下のようになってて、shape=(25000, 128)が2つある。

[array([[ 101, 1996, 2466, ...,    0,    0,    0],
        [ 101, 1037, 2158, ..., 4287, 1996,  102],
        [ 101, 1045, 2001, ..., 2023, 2499,  102],
        ...,
        [ 101, 7929, 2182, ..., 7987, 1013,  102],
        [ 101, 2672, 1045, ..., 1999, 1996,  102],
        [ 101, 1996, 3213, ..., 2046, 1037,  102]]),
 array([[0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        ...,
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0],
        [0, 0, 0, ..., 0, 0, 0]])]


学習済みBERTのload & 2値分類用に再構築

inputs = bert.inputs[:2]
dense = bert.get_layer('NSP-Dense').output
outputs = keras.layers.Dense(units=2, activation='softmax')(dense)
# rebuild BERT model 
model = keras.models.Model(inputs, outputs)


違うのは最後の出力層だけ

Encoder-12-FeedForward-Norm (La (None, 128, 768)     1536        Encoder-12-FeedForward-Add[0][0] 
__________________________________________________________________________________________________
Extract (Extract)               (None, 768)          0           Encoder-12-FeedForward-Norm[0][0]
__________________________________________________________________________________________________
NSP-Dense (Dense)               (None, 768)          590592      Extract[0][0]                    
__________________________________________________________________________________________________
dense (Dense)                   (None, 2)            1538        NSP-Dense[0][0]  

正解率

88%で転移学習でも十分高性能。



まとめ & BERTの全体像

今回はBERTで転移学習をしてみた。BERT専用データセットじゃなく一般公開用のを使ったから、かなり勉強になった。

keras_bertを使えば、modelを改造することで、様々な入力形式の自然言語処理タスクで高性能が出せるようだ。

参考サイトKeras BERTでファインチューニングしてみる


BERT全体像


Layer (type)                    Output Shape         Param #     Connected to                     
==================================================================================================
Input-Token (InputLayer)        (None, 128)          0                                            
__________________________________________________________________________________________________
Input-Segment (InputLayer)      (None, 128)          0                                            
__________________________________________________________________________________________________
Embedding-Token (TokenEmbedding [(None, 128, 768), ( 23440896    Input-Token[0][0]                
__________________________________________________________________________________________________
Embedding-Segment (Embedding)   (None, 128, 768)     1536        Input-Segment[0][0]              
__________________________________________________________________________________________________
Embedding-Token-Segment (Add)   (None, 128, 768)     0           Embedding-Token[0][0]            
                                                                 Embedding-Segment[0][0]          
__________________________________________________________________________________________________
Embedding-Position (PositionEmb (None, 128, 768)     98304       Embedding-Token-Segment[0][0]    
__________________________________________________________________________________________________
Embedding-Dropout (Dropout)     (None, 128, 768)     0           Embedding-Position[0][0]         
__________________________________________________________________________________________________
Embedding-Norm (LayerNormalizat (None, 128, 768)     1536        Embedding-Dropout[0][0]          
__________________________________________________________________________________________________
Encoder-1-MultiHeadSelfAttentio (None, 128, 768)     2362368     Embedding-Norm[0][0]             
__________________________________________________________________________________________________
Encoder-1-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-1-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-1-MultiHeadSelfAttentio (None, 128, 768)     0           Embedding-Norm[0][0]             
                                                                 Encoder-1-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-1-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-1-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-1-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-1-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-1-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-1-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-1-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-1-MultiHeadSelfAttention-
                                                                 Encoder-1-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-1-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-1-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-2-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-1-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-2-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-2-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-2-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-1-FeedForward-Norm[0][0] 
                                                                 Encoder-2-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-2-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-2-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-2-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-2-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-2-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-2-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-2-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-2-MultiHeadSelfAttention-
                                                                 Encoder-2-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-2-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-2-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-3-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-2-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-3-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-3-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-3-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-2-FeedForward-Norm[0][0] 
                                                                 Encoder-3-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-3-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-3-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-3-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-3-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-3-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-3-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-3-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-3-MultiHeadSelfAttention-
                                                                 Encoder-3-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-3-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-3-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-4-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-3-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-4-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-4-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-4-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-3-FeedForward-Norm[0][0] 
                                                                 Encoder-4-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-4-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-4-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-4-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-4-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-4-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-4-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-4-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-4-MultiHeadSelfAttention-
                                                                 Encoder-4-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-4-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-4-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-5-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-4-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-5-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-5-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-5-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-4-FeedForward-Norm[0][0] 
                                                                 Encoder-5-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-5-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-5-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-5-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-5-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-5-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-5-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-5-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-5-MultiHeadSelfAttention-
                                                                 Encoder-5-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-5-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-5-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-6-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-5-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-6-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-6-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-6-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-5-FeedForward-Norm[0][0] 
                                                                 Encoder-6-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-6-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-6-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-6-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-6-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-6-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-6-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-6-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-6-MultiHeadSelfAttention-
                                                                 Encoder-6-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-6-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-6-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-7-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-6-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-7-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-7-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-7-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-6-FeedForward-Norm[0][0] 
                                                                 Encoder-7-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-7-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-7-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-7-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-7-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-7-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-7-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-7-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-7-MultiHeadSelfAttention-
                                                                 Encoder-7-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-7-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-7-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-8-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-7-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-8-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-8-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-8-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-7-FeedForward-Norm[0][0] 
                                                                 Encoder-8-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-8-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-8-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-8-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-8-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-8-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-8-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-8-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-8-MultiHeadSelfAttention-
                                                                 Encoder-8-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-8-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-8-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-9-MultiHeadSelfAttentio (None, 128, 768)     2362368     Encoder-8-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-9-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-9-MultiHeadSelfAttention[
__________________________________________________________________________________________________
Encoder-9-MultiHeadSelfAttentio (None, 128, 768)     0           Encoder-8-FeedForward-Norm[0][0] 
                                                                 Encoder-9-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-9-MultiHeadSelfAttentio (None, 128, 768)     1536        Encoder-9-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-9-FeedForward (FeedForw (None, 128, 768)     4722432     Encoder-9-MultiHeadSelfAttention-
__________________________________________________________________________________________________
Encoder-9-FeedForward-Dropout ( (None, 128, 768)     0           Encoder-9-FeedForward[0][0]      
__________________________________________________________________________________________________
Encoder-9-FeedForward-Add (Add) (None, 128, 768)     0           Encoder-9-MultiHeadSelfAttention-
                                                                 Encoder-9-FeedForward-Dropout[0][
__________________________________________________________________________________________________
Encoder-9-FeedForward-Norm (Lay (None, 128, 768)     1536        Encoder-9-FeedForward-Add[0][0]  
__________________________________________________________________________________________________
Encoder-10-MultiHeadSelfAttenti (None, 128, 768)     2362368     Encoder-9-FeedForward-Norm[0][0] 
__________________________________________________________________________________________________
Encoder-10-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-10-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-10-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-9-FeedForward-Norm[0][0] 
                                                                 Encoder-10-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-10-MultiHeadSelfAttenti (None, 128, 768)     1536        Encoder-10-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-10-FeedForward (FeedFor (None, 128, 768)     4722432     Encoder-10-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-10-FeedForward-Dropout  (None, 128, 768)     0           Encoder-10-FeedForward[0][0]     
__________________________________________________________________________________________________
Encoder-10-FeedForward-Add (Add (None, 128, 768)     0           Encoder-10-MultiHeadSelfAttention
                                                                 Encoder-10-FeedForward-Dropout[0]
__________________________________________________________________________________________________
Encoder-10-FeedForward-Norm (La (None, 128, 768)     1536        Encoder-10-FeedForward-Add[0][0] 
__________________________________________________________________________________________________
Encoder-11-MultiHeadSelfAttenti (None, 128, 768)     2362368     Encoder-10-FeedForward-Norm[0][0]
__________________________________________________________________________________________________
Encoder-11-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-11-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-11-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-10-FeedForward-Norm[0][0]
                                                                 Encoder-11-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-11-MultiHeadSelfAttenti (None, 128, 768)     1536        Encoder-11-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-11-FeedForward (FeedFor (None, 128, 768)     4722432     Encoder-11-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-11-FeedForward-Dropout  (None, 128, 768)     0           Encoder-11-FeedForward[0][0]     
__________________________________________________________________________________________________
Encoder-11-FeedForward-Add (Add (None, 128, 768)     0           Encoder-11-MultiHeadSelfAttention
                                                                 Encoder-11-FeedForward-Dropout[0]
__________________________________________________________________________________________________
Encoder-11-FeedForward-Norm (La (None, 128, 768)     1536        Encoder-11-FeedForward-Add[0][0] 
__________________________________________________________________________________________________
Encoder-12-MultiHeadSelfAttenti (None, 128, 768)     2362368     Encoder-11-FeedForward-Norm[0][0]
__________________________________________________________________________________________________
Encoder-12-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-12-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-12-MultiHeadSelfAttenti (None, 128, 768)     0           Encoder-11-FeedForward-Norm[0][0]
                                                                 Encoder-12-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-12-MultiHeadSelfAttenti (None, 128, 768)     1536        Encoder-12-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-12-FeedForward (FeedFor (None, 128, 768)     4722432     Encoder-12-MultiHeadSelfAttention
__________________________________________________________________________________________________
Encoder-12-FeedForward-Dropout  (None, 128, 768)     0           Encoder-12-FeedForward[0][0]     
__________________________________________________________________________________________________
Encoder-12-FeedForward-Add (Add (None, 128, 768)     0           Encoder-12-MultiHeadSelfAttention
                                                                 Encoder-12-FeedForward-Dropout[0]
__________________________________________________________________________________________________
Encoder-12-FeedForward-Norm (La (None, 128, 768)     1536        Encoder-12-FeedForward-Add[0][0] 
__________________________________________________________________________________________________
MLM-Dense (Dense)               (None, 128, 768)     590592      Encoder-12-FeedForward-Norm[0][0]
__________________________________________________________________________________________________
MLM-Norm (LayerNormalization)   (None, 128, 768)     1536        MLM-Dense[0][0]                  
__________________________________________________________________________________________________
Extract (Extract)               (None, 768)          0           Encoder-12-FeedForward-Norm[0][0]
__________________________________________________________________________________________________
MLM-Sim (EmbeddingSimilarity)   (None, 128, 30522)   30522       MLM-Norm[0][0]                   
                                                                 Embedding-Token[0][1]            
__________________________________________________________________________________________________
Input-Masked (InputLayer)       (None, 128)          0                                            
__________________________________________________________________________________________________
NSP-Dense (Dense)               (None, 768)          590592      Extract[0][0]                    
__________________________________________________________________________________________________
MLM (Masked)                    (None, 128, 30522)   0           MLM-Sim[0][0]                    
                                                                 Input-Masked[0][0]               
__________________________________________________________________________________________________
NSP (Dense)                     (None, 2)            1538        NSP-Dense[0][0]          

高性能版GANの「styleGAN」で本物そっくりの画像を生成してみた【keras・機械学習】

今回は論文で紹介されてたNVIDIAが開発したstyleGANを実装してみた。

普通のGANとは生成過程も違うし、生成画像の出来の精度も比較にならないぐらい高くて、驚いた。
仕事で使う機会があったので、その生成過程をまとめてく。


目次
1.styleGANについて
2.styleGANコード詳細
3.訓練
4.生成画像
5.まとめ


1.styleGANについて

styleGANはNVIDIAが開発した、本物と見分けがつかないくらいの画像が作れる、超高精度のGAN。
qiita.com


"Progressive-Growing of GANs”というGANの亜種のgeneratorの部分を発展させたもの。

メインのメカニズムは、低い解像度の層から順に学習して、高精度の画像の生成していく仕組みらしい。


【従来のGANとstyleGANの違い】
f:id:trafalbad:20190707152347j:plain

generatorの部分がAdaptive Instance Normalization (AdaIN)でかなり改造してあるのが普通のGANと大きく異なる点。

ちなみにdiscriminatorは普通のGANと同じ。


【Generator部分】
f:id:trafalbad:20190707152336j:plain




2.styleGANコード詳細

input画像



入力画像はgoogle検索でもってきた景色の画像3枚を120枚に増幅。
f:id:trafalbad:20190707154813p:plain


Macの動画ソフト「Quick time player」で動画にして、ffmpegで静止画に変換。120枚くらいに増やした。

$ ffmpeg -i 元動画.avi -ss 144 -t 148 -r 24 -f image2 %06d.jpg

かなり簡単に画像が大量に作れるので便利。

他にもopencvで動画から、静止画を作るやり方もある。

input画像等、ハイパーパラメータの条件はこんな感じ

・画像の合計120枚

・shape=(256, 256, 3)

・Batch =10

・255で割って正規化

・1000~2000エポック

各景色画像30枚ずつで、計120枚を10batchずつ回して訓練してく。

ちなみにoptimizerを含めて、今回のパラメータは、サイズが256の時にこのパラメータでうまくいった。

けど、512とか1024は同じパラメータでは上手くいくかわからない。




Generator



generatorはinputが3つあって、2つは論文にあるように、ノイズを入れるところになってる。

AdaINが普通のGANとの違いがでかい。

from AdaIN import AdaInstanceNormalization

im_size = 256
latent_size = 512

def g_block(inp, style, noise, fil, u = True):

    b = Dense(fil)(style)
    b = Reshape([1, 1, fil])(b)
    g = Dense(fil)(style)
    g = Reshape([1, 1, fil])(g)

    n = Conv2D(filters = fil, kernel_size = 1, padding = 'same', kernel_initializer = 'he_normal')(noise)

    if u:
        out = UpSampling2D(interpolation = 'bilinear')(inp)
        out = Conv2D(filters = fil, kernel_size = 3, padding = 'same', kernel_initializer = 'he_normal')(out)
    else:
        out = Activation('linear')(inp)

    out = AdaInstanceNormalization()([out, b, g])
    out = add([out, n])
    out = LeakyReLU(0.01)(out)

    b = Dense(fil)(style)
    b = Reshape([1, 1, fil])(b)
    g = Dense(fil)(style)
    g = Reshape([1, 1, fil])(g)

    n = Conv2D(filters = fil, kernel_size = 1, padding = 'same', kernel_initializer = 'he_normal')(noise)

    out = Conv2D(filters = fil, kernel_size = 3, padding = 'same', kernel_initializer = 'he_normal')(out)
    out = AdaInstanceNormalization()([out, b, g])
    out = add([out, n])
    out = LeakyReLU(0.01)(out)

    return out

def generator():

    inp_s = Input(shape = [latent_size])
    sty = Dense(512, kernel_initializer = 'he_normal')(inp_s)
    sty = LeakyReLU(0.1)(sty)
    sty = Dense(512, kernel_initializer = 'he_normal')(sty)
    sty = LeakyReLU(0.1)(sty)

    inp_n = Input(shape = [im_size, im_size, 1])
    noi = [Activation('linear')(inp_n)]
    curr_size = im_size
    while curr_size > 4:
        curr_size = int(curr_size / 2)
        noi.append(Cropping2D(int(curr_size/2))(noi[-1]))

    inp = Input(shape = [1])
    x = Dense(4 * 4 * 512, kernel_initializer = 'he_normal')(inp)
    x = Reshape([4, 4, 512])(x)
    x = g_block(x, sty, noi[-1], 512, u=False)

    if(im_size >= 1024):
        x = g_block(x, sty, noi[7], 512) # Size / 64
    if(im_size >= 512):
        x = g_block(x, sty, noi[6], 384) # Size / 64
    if(im_size >= 256):
        x = g_block(x, sty, noi[5], 256) # Size / 32
    if(im_size >= 128):
        x = g_block(x, sty, noi[4], 192) # Size / 16
    if(im_size >= 64):
        x = g_block(x, sty, noi[3], 128) # Size / 8

    x = g_block(x, sty, noi[2], 64) # Size / 4
    x = g_block(x, sty, noi[1], 32) # Size / 2
    x = g_block(x, sty, noi[0], 16) # Size
    x = Conv2D(filters = 3, kernel_size = 1, padding = 'same', activation = 'sigmoid')(x)
    return Model(inputs = [inp_s, inp_n, inp], outputs = x)


Discriminator



Discriminatorは従来のと変わらない。

今回は、1024サイズにも対応できるようにするために、有名なGANのDiscriminatorを使った。

def discriminator():
    inp = Input(shape = [im_size, im_size, 3])

    x = d_block(inp, 16) #Size / 2
    x = d_block(x, 32) #Size / 4
    x = d_block(x, 64) #Size / 8

    if (im_size > 32):
       x = d_block(x, 128) #Size / 16

    if (im_size > 64):
        x = d_block(x, 192) #Size / 32

    if (im_size > 128):
        x = d_block(x, 256) #Size / 64

    if (im_size > 256):
        x = d_block(x, 384) #Size / 128

    if (im_size > 512):
        x = d_block(x, 512) #Size / 256

    x = Flatten()(x)
    x = Dense(128)(x)
    x = Activation('relu')(x)
    x = Dropout(0.6)(x)
    x = Dense(1)(x)
    
    return Model(inputs = inp, outputs = x)


disganとadganの実装


disganでdiscriminatorを訓練。adganでgeneratorを訓練する。

G = generator()
D = discriminator()

# Dは更新して、Gは更新しない
D.trainable = True
for layer in D.layers:
    layer.trainable = True

G.trainable = False
for layer in G.layers:
    layer.trainable = False

ri = Input(shape = [im_size, im_size, 3])
dr = D(ri)

gi = Input(shape = [latent_size])
gi2 = Input(shape = [im_size, im_size, 1])
gi3 = Input(shape = [1])
df = D(G([gi, gi2, gi3]))
da = D(ri)
disgan = Model(inputs=[ri, gi, gi2, gi3], outputs=[dr, df, da])

# Gは更新、Dは更新しない
D.trainable = False
for layer in D.layers:
    layer.trainable = False

G.trainable = True
for layer in G.layers:
    layer.trainable = True

gi = Input(shape = [latent_size])
gi2 = Input(shape = [im_size, im_size, 1])
gi3 = Input(shape = [1])
df = D(G([gi, gi2, gi3]))
adgan = Model(inputs = [gi, gi2, gi3], outputs = df)



lossとoptimizer


lossはGANには珍しくMSEを使った。

optimizerはAdamで学習率(lr)は下のように設定。

・Disgan => lr=0.0002

・Adage => lr=0.0001

両方0.0001よりdisganを少し大きくした方が精度がいい結果になった。

def gradient_penalty_loss(y_true, y_pred, averaged_samples, weight):
    gradients = K.gradients(y_pred, averaged_samples)[0]
    gradients_sqr = K.square(gradients)
    gradient_penalty = K.sum(gradients_sqr,
                              axis=np.arange(1, len(gradients_sqr.shape)))
    return K.mean(gradient_penalty * weight)

partial_gp_loss = partial(gradient_penalty_loss, averaged_samples = ri, weight = 5)

disgan.compile(optimizer=Adam(lr=0.0002, beta_1 = 0, beta_2 = 0.99, decay = 0.00001), loss=['mse', 'mse', partial_gp_loss])

adgan.compile(optimizer = Adam(lr=0.0001, beta_1 = 0, beta_2 = 0.99, decay = 0.00001), loss = 'mse')

3.訓練

主にdisganとadganのtrain。
従来のGANと訓練方法は同じだけど、generatorが違う分、disganとadgan共に独特な値を入れてる。

# Noise 
def noise(n):
    return np.random.normal(0.0, 1.0, size = [n, latent_size])

def noiseImage(n):
    return np.random.uniform(0.0, 1.0, size = [n, im_size, im_size, 1])

# train disgan
real_images  = x_train[idx*batch_size:(idx+1)*batch_size]
train_data = [real_images, noise(batch_size), noiseImage(batch_size), np.ones((batch_size, 1))]
d_loss = disgan.train_on_batch(train_data, [np.ones((batch_size, 1)),
                                                      -np.ones((batch_size, 1)), np.ones((batch_size, 1))])

# train adgan
g_loss = adgan.train_on_batch([noise(batch_size), noiseImage(batch_size), np.ones((batch_size, 1))], 
                                        np.zeros((batch_size, 1), dtype=np.float32))


4.生成画像

生成した画像はこんな感じ。論文通り、本物とそっくりでびっくりの超高精度。

【生成画像】f:id:trafalbad:20190707155211p:plain

生成過程は、初めは黒から始まって、ゴッホみたいな油絵みたいになっていき、本物に近づいてく感じ。


f:id:trafalbad:20190707155057g:plain




5.まとめ

同じ画像が多いとかなり早くできる。今回は3枚×40=120枚で、700エポックにはもう本物っぽいのが出来始めてた。


ただバリエーションが増えると(例えば100枚のスニーカーで、100枚全部違う画像の場合)、とんでもなく時間がかかる。

なるべく生成したい画像は同じやつを何枚も入れとくと早く生成できる。論文みたいな顔のバリエーションだと、とんでもない時間(10万エポックくらい)かかる気がする。

GANの訓練はかなり繊細なので、styleGANのパラメータ調整は普通のGAN以上に繊細だった。

Unet(Auto encoder)でスニーカー画像からロゴを生成してみた【機械学習】

よく元画像から別の画像を生成したりするのに使うautoencoderの亜種「Unet」を使ってみた。
今回やるのはadidasのスニーカーを入力して、ロゴを出力するように学習させるタスク。

autoencoderを使うのは初めてなので、作業過程などをまとめてく。

目次
1.概要
2.コード部分
3.生成画像
・まとめ

1.概要


autoencoderは簡単に言うと画像とか文章を入力して、別のものに変換して出力するモデル。

例は、画像→画像、文章→文章、画像→文章、みたいにたくさんある。

f:id:trafalbad:20190703072233j:plain


Unetはautoencoderの一種。

U-Netが強力なのはEncoderとDecoderとの間に「Contracting path(スキップコネクション)」があるからで、Residual Network(ResNet)と同じ効果を発揮するらしい。

f:id:trafalbad:20190703072149j:plain


ResNetを多様するDeep Unetもあるらしいが、今回は普通のUnetで試した。

今回試すことは、「adidasのスニーカー画像を入力→adidasのロゴ画像を出力する」タスクをやってみた。

adidasのスニーカー】
f:id:trafalbad:20190703072311p:plain




adidasのロゴ
f:id:trafalbad:20190703072335p:plain




2.コード部分

Unet

主に変えた部分は、

・層を二つ追加した
・BatchNormalizationを全層に加えた

の2点。

INPUT_SIZE =(256,256,3)
def unet(input_size = INPUT_SIZE):
    inputs = Input(input_size)

    conv1_1 = Conv2D(32, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(inputs)
    conv1_1 = Conv2D(32, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1_1)
    norm1_1 =  BatchNormalization()(conv1_1)
    pool1_1 = MaxPooling2D(pool_size=(2, 2))(norm1_1)

    conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1_1)
    conv1 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv1)
    norm1 =  BatchNormalization()(conv1)
    pool1 = MaxPooling2D(pool_size=(2, 2))(norm1)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool1)
    conv2 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv2)
    norm2 = BatchNormalization()(conv2)
    pool2 = MaxPooling2D(pool_size=(2, 2))(norm2)
    conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool2)
    conv3 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv3)
    norm3 = BatchNormalization()(conv3)
    pool3 = MaxPooling2D(pool_size=(2, 2))(norm3)
    conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool3)
    conv4 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv4)
    norm4 = BatchNormalization()(conv4)
    pool4 = MaxPooling2D(pool_size=(2, 2))(norm4)

    conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(pool4)
    conv5 = Conv2D(1024, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv5)
    norm5 = BatchNormalization()(conv5)

    up6 = Conv2D(512, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(norm5))
    merge6 = concatenate([norm4,up6], axis = 3)
    conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge6)
    conv6 = Conv2D(512, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv6)
    norm6 = BatchNormalization()(conv6)

    up7 = Conv2D(256, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(norm6))
    merge7 = concatenate([norm3,up7], axis = 3)
    conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge7)
    conv7 = Conv2D(256, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv7)
    norm7 = BatchNormalization()(conv7)

    up8 = Conv2D(128, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(norm7))
    merge8 = concatenate([norm2,up8], axis = 3)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge8)
    conv8 = Conv2D(128, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv8)
    norm8 = BatchNormalization()(conv8)

    up9 = Conv2D(64, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(norm8))
    merge9 = concatenate([norm1,up9], axis = 3)
    conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9)
    conv9 = Conv2D(64, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
    conv9 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9)
    norm9 = BatchNormalization()(conv9)

    up9_1 = Conv2D(32, 2, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(UpSampling2D(size = (2,2))(norm9))
    merge9_1 = concatenate([norm1_1, up9_1], axis = 3)
    conv9_1 = Conv2D(32, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(merge9_1)
    conv9_1 = Conv2D(32, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9_1)
    conv9_1 = Conv2D(2, 3, activation = 'relu', padding = 'same', kernel_initializer = 'he_normal')(conv9_1)
    norm9_1 = BatchNormalization()(conv9_1)

    conv10 = Conv2D(3, 1, activation = 'sigmoid')(conv9_1)
    return Model(input = inputs, output = conv10)


Contracting pathがあるので、層を厚くしても特徴抽出の効果が増した気がする。





画像入力



今回は画像は増幅できないので、kerasのImagegeneratorメソッドは使わず、generatorを自作。

class trainGenerator(object):
    def __init__(self):
        self.img_generator=[]
        self.mask_generator=[]
        self.reset()
   
    def reset(self):
        self.img_generator=[]
        self.mask_generator=[]
      
    def flow_from_dir(self, img_path, mask_path, batch_size=10):
        while True:
            for imgs, mask in zip(os.listdir(img_path), os.listdir(mask_path)):
                imgs=cv2.imread(img_path+'/'+imgs)
                mask=cv2.imread(mask_path+'/'+mask)
                if imgs is not None:
                  self.img_generator.append(imgs/ 255)
                if mask is not None:
                  self.mask_generator.append(mask/ 255)
                    
                  if len(self.img_generator)==batch_size:
                      input_img=np.array(self.img_generator, dtype=np.float32)
                  if len(self.mask_generator)==batch_size:
                      input_mask=np.array(self.mask_generator, dtype=np.float32)
                      self.reset()
                      yield input_img, input_mask

class valGenerator(trainGenerator):
    def __init__(self):
        self.img_generator=[]
        self.mask_generator=[]
        self.reset()

flow_from_dirのimg_pathが入力、mask_pathが出力画像の教師画像になってる。

validationデータ用のvalGeneratorクラスはtrainGeneratorクラスを継承してる。
あと、指定したバッチサイズに切り出されるようにした。

これでmodel.fitgenerator()メソッドに入れればOK。




訓練部分


lossは、Unetで頻繁に使われるDice loss(dice_coef_loss)を使った。optimizerは代表的なAdam。

import keras.backend as K
import tensorflow as tf
import numpy as np

# loss function
def dice_coef_loss(y_true, y_pred):
    return 1.0 - dice_coef(y_true, y_pred)


def dice_coef(y_true, y_pred):
    y_true = K.flatten(y_true)
    y_pred = K.flatten(y_pred)
    intersection = K.sum(y_true * y_pred)
    return 2.0 * intersection / (K.sum(y_true) + K.sum(y_pred) + 1)

あと、keras.callbackに自作のクラスを入れて、5エポックごとにloss曲線のpng画像が保存されるように工夫した。

from dice_coefficient_loss import dice_coef_loss, dice_coef
from HistoryCallbackLoss import HistoryCheckpoint

callback=[]
callback.append(HistoryCheckpoint(filepath='tb/LearningCurve_{history}.png', verbose=1, period=10))
callback.append(TensorBoard(log_dir='tb/'))
callback.append(ModelCheckpoint('logss/{epoch:02d}_unet.hdf5', monitor='loss', verbose=1))

model = unet()
# model.load_weights("10_unet.hdf5")
model.compile(loss=dice_coef_loss, optimizer=Adam(lr=1e-4), metrics=[dice_coef])           
model.summary()

model.fit_generator(training, steps_per_epoch=2000, 
                    epochs=10,
                    callbacks=callback,
                    validation_data=validations, 
                    validation_steps=200)


工夫した点



今回はよく論文に登場する精度の高い「〜ネットワーク」みたいな、飛び道具的なアルゴリズムを使わずに、ニューラルネットワーク自体を変えて、精度改善の工夫をした。


・Unetの層を増やす

・Dice lossを使う

・validation dataを使う

・callbackメソッドを使い、損失関数を自動調節する関数を作成

・正規化→L1, L2正規化、画像を255で割る

の5点を主に工夫した。派手なことせず、地道なテクで、かなり精度が上がった。

ニューラルネット系は人気で精度が高い「飛び道具」的なアルゴリズムでも、実際に実装してみると、全然使えないこともある。

なので、臨機応変に使いこなしたり、ニューラルネットワーク自体をいじって、堅実に精度向上の工夫ができるテクニックは必須。




3.生成画像

生成した画像

f:id:trafalbad:20190703072405p:plain


出来が少し荒い反省点としては、


・真っ黒の画像をいくつか訓練データに含んでしまった(訓練データが荒い)

・訓練枚数(140枚)少ない、訓練回数が少ない

とかが原因。

画像枚数1万枚以上とか、ガチでやってる人のUnetの結果はすごいのが多い。

なので、逆に140枚でこれだけお手軽に結構な精度の画像が生成できるのはすごい。




まとめ

今回はautoencoderのUnetを試してみた。

ガチでニューラルネットワーク自体を変えて精度向上の工夫したのは初めだった。

Variational Autoencoder(VAE)を使った記事も機会があれば書こうと思う。

circleCIとgithubを連携して、簡単にコードをGCRにpushできるようにしてみた

コードとかの管理ツールとして、circleCIを使っている。

というのもcircleCIを使えば、コードを書き換えて、githubにuploadすれば、circleCI経由で勝手にGCRにDocker imageがpushされるから。

簡単に言うと便利すぎるから使ってる感じ。

今回、githubとcircleCIを連携して、GCRにdocker pushするまでの過程をまとめた。

目次
・circleCIの何が美味しいの?

1.githubとcircleCIを連携させる

2.Cloud SDKやprojectの設定

3.GCRのAPIを有効にし、発行したJSONキーをcircleCIの環境変数に登録

4.発行したJSONキーをcircleCIに登録

5.githubに.circleci/config.ymlファイルを作成

6.コードをgithubにuploadして、GCRにPushする

circleCIの何が美味しいの?

circleCIを使えば、各APIを利用し、コード管理、自動化とかデプロイまでの作業を簡単にしたりと、いろんなことができる。

circleCIの自動化の利点は図で表すとこんな感じ

circleCI導入前
sshでログインしてデプロイコマンドを叩く必要あり
f:id:trafalbad:20190531141822j:plain


circleCI導入後
デプロイコマンド1つでデプロイが完了
f:id:trafalbad:20190531141451j:plain

個人的にTerraform+github+circleCI+atlasでのインフラ管理(記事)がとても便利で好き。

自分はコードを変更するたびにdocker imageを作成してdocker pushするのが面倒すぎたので、
github+circleCI+GCRで連携
→変更したコードをgithubにあげる
→GCRにdocker pushされる

使い方をした。以下ではそのやり方を書いてく。



1.githubとcircleCIを連携させる

circleCIのサイトに行き、githubアカウントで連携

f:id:trafalbad:20190531141342j:plain

連携方法はこのサイトを参考にした。

主にgithubの連携したいレポジトリを選択して、プランを決めれる。
普通に使うなら、プランはフリーでOK。


2.Cloud SDKやprojectの設定

自分はローカルから使う環境だったので、cloud SDK(gcloudコマンド)をinstallして、GCPのprojectを指定した。

# gcloudコマンド のインストール 
$ curl https://sdk.cloud.google.com | bash
$ exec -l $SHELL

# 認証
$ gcloud init

# 権限承認
$ gcloud auth login

# project認証
$ gcloud config set project PROJECT_ID


3.GCRのAPIを有効にし、発行したJSONキーをcircleCIの環境変数に登録

GCPのコンソール画面から、左の「IAMと管理」→「サービスアカウント」を選択し、アカウント作成。

IAMサービスの「サービスアカウント作成」をクリックして(名前・パスワードは適当)→役割を「ストレージ」にして作成→JSONキーを発行。

左の項目のStorageから、「artifacts.[プロジェクト名].appspot.com」のバケットを選択して、権限ボタンをクリック。

さっき作成したIAMサービスアカウントにそのバケットの「ストレージオブジェクト閲覧者」の権限を付与する。





4.発行したJSONキーをcircleCIに登録

circleCIにアクセスし、左の"ADD PROJECTS"ボタンから連携したgithubのレポジトリを選択し、"Set Up Project"をクリック

f:id:trafalbad:20190531141913j:plain



そのあと、Environment Valiableの「Add variable」をクリック。
f:id:trafalbad:20190531141929j:plain



NAMEは"GCLOUD_SERVICE_KEY"と入力、VALUEにさっき発行したJSONキーの文字列をコピペ。

f:id:trafalbad:20190531141948j:plain




5.githubに.circleci/config.ymlファイルを作成

githubで直接「Create new file」で選択して、githubに".circleci/config.yml"ファイルを作成。

config.ymlのコード内容はこのサイトを参考にした。


自分のconfig.yml

version: 2
jobs:
  build:
    docker:
      - image: google/cloud-sdk
      
    working_directory: /go/src/github.com/[githubのアカウント名]/[対象レポジトリ名]
    steps:
      - checkout
      - setup_remote_docker:
          version: 18.06.0-ce
      - run:
          name: Setup CLOUD SDK
          command: |
            echo $GCLOUD_SERVICE_KEY | gcloud auth activate-service-account --key-file=-
            gcloud --quiet config set project [PROJECT_ID]
            gcloud --quiet config set compute/zone asia-east1-b
            gcloud --quiet auth configure-docker
      - run:
          name: Push docker image
          command: |
            docker build -t go-web-server .
            LATEST_TAG=gcr.io/[PROJECT_ID]/go-web-server:latest
            docker tag go-web-server $LATEST_TAG
            docker push $LATEST_TAG            

6.コードをgithubにuploadして、GCRにPushする

今回はGoの簡単なアプリ用コードをgithubにupload。

Dockerfile

FROM golang:1.11-alpine
WORKDIR /go/src/github.com/naoty/go-web-server
COPY . .
RUN go install github.com/naoty/go-web-server
CMD ["go-web-server"]

main.go

package main
import (
  "fmt"
  "net/http"
)

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello")
  })
  http.ListenAndServe(":8080", nil)
}


このコードをgit pushでアップロード

$ git add .
$ git commit -m “コメント”
$ git push origin master

git pushコマンドとcircleCIが連動して、GCRにdocker pushされんのかな。

circleCIで”SUCCESS”が出て、
f:id:trafalbad:20190531142018p:plain



GCRにちゃんとdocker imageがpushされてる。
f:id:trafalbad:20190531142035p:plain


circleCIはECRとかいろんなデプロイツールとして連携できるので、おすすめ。

Terraform × ApexでLambdaとCloudwatchログのスケジュールを管理してみた【機械学習・AWSインフラ】

Terraformが人気のインフラ管理ツールということで是非習得したかったので、TerraformとそのラッパーのApexを使って、LambdaとCloudwatchログのスケジュール管理をしてみた。

Apexを使えばLambda管理がラクにできるので、運用をイメージしてTerraformでlambdaを含めたAWSインフラを管理するまでのログをまとめてく。

目次
1.Terraformで管理するインフラの全体像
2.コード&ファイル
3.実行コマンド
4.まとめ

1.Terraformで管理するインフラの全体像

AWSインフラリソースの全体像


TerraformとApexで管理するAWSインフラの全体像を表すと下の図の通り。
f:id:trafalbad:20190518092716j:plain


Apexを使うと嬉しいことは2つ。

・Lambdaの管理がラクになる
terraformでLambdaを管理するには、

AWSのオブジェクト管理(Lambda Function、IAM、triggerのためのCloudWatch Eventsなど)

②Lambda関数のソースコード

③Lambda関数のソースコードに依存するライブラリたち

ソースコードとライブラリを固めたzip

が必要だけど、Apexなら②~④をまとめてやってくれる。


・Lambda用のコードをS3バケットではなく、直接Lambdaにアップロードできる
Apexが上の②〜④をやってくれるおかげで、zipを入れるS3バケットの管理や、terraformでのzipファイルの管理が必要なくなる。




運用イメージ


運用イメージは
・CloudWatchログのログをLambdaで「他のリソース」に送りつけること
を想定してる。


例1.Datadog docsにCloudWatchログのログをLambdaで送りつける
f:id:trafalbad:20190518083246p:plain



例2.ElasticseachにCloudWatchログのログをLambdaで送りつける
f:id:trafalbad:20190518083306j:plain


2.コード

ディレクトリ構成

./project.json # プロジェクト全体の定義
./functions/datadog_logs/function.json # 関数の定義
./functions/datadog_logs/datadog_logs.py # Lambdaで実行するコード(今回はPythonのサンプル)
./infrastructure/prod/main.tf # Terraformで管理するAWSリソースの定義</div>

functions配下の「datadog_logs」フォルダはLambda関数名に合わせる。
またinfrastructure配下の「prod」フォルダは環境の意味(prod=env)で、環境ごとにLambdaを作り分けられるApexの機能。

f:id:trafalbad:20190518083333p:plain



project.json


{
    "name": "event_driven_job",
    "description": "event driven job",
    "memory": 1024,
    "timeout": 120,
    "role": "arn:aws:iam::{各自のAWS Account ID}:role/datadog_logs",
    "environment": {}
}

fuction.jsonとproject.jsonの共通項目はproject.jsonに書けば上書きできるので、role、memory、timeoutとかの共通項目はproject.jsonに書いた。



function.json


{
    "description": "datadog_logs",
    "runtime": "python3.6",
    "handler": "datadog_logs.lambda_handler",
    "environment":
    {
        "ENV": "dev"
    }
}

“runtime”と”handler"さえ良ければOK。あと、関数名は「datadog_logs」。



datadog_logs.py


def lambda_handler(event, context):
    json = {"statusCode": 200, "body": "hello world"}
    return json

Lambda関数用のpython.3.6のサンプルファイル。今回の記事ではほとんど関係ないのでテキトーに作った。



main.tf(resource、variableとかの解説)


・インフラ管理リソース設定
terraform

provider “aws

・Apexに必要
variable "apex_function_datadog_logs" {}


・IAMロールの作成に必要
resource "aws_iam_role" “datadog_logs_lambda_role"

data "aws_iam_policy_document" "datadog_logs_lambda_policy_doc"

resource "aws_iam_role_policy" “datadog_logs_lambda_policy"


・Cloudwatchログのログの出力先
output “datadog_logs_lambda_role"


・Cloudwatchログのロググループ名を定義
data "aws_cloudwatch_log_group" “log_group"


・Cloudwatchログからのログのストリーム設定
resource "aws_cloudwatch_log_subscription_filter" “datadog_logs_filter"


・Lambdaへのアクセス許可
resource "aws_lambda_permission" “datadog_logs_filter"





3.実行コマンド

1.セットアップ

MacにApexをinstall

$ curl https://raw.githubusercontent.com/apex/apex/master/install.sh | sh

・cloudwatchログにロググループ作成(参考サイト
グループ名→「datadog_logs」



・東京regionのS3バケット「apex-test-bucket」を作る


AWS環境変数の設定

$ export AWS_ACCESS_KEY_ID=****
$ export AWS_SECRET_ACCESS_KEY=****
$ export AWS_DEFAULT_REGION=ap-northeast-1
$ export AWS_REGION=ap-northeast-1


2.terraformのバックグランド初期化


$ apex infra init


3.IAMロールの作成


$ apex infra apply -target aws_iam_role.datadog_logs_lambda_role -target aws_iam_policy_document.datadog_logs_lambda_policy_doc -target aws_iam_role_policy.datadog_logs_lambda_policy

-targetオプションではmain.tfのIAMロール作成に必要なリソースを指定してる。

実行後は、IAMロールで「data_logs (AWS サービス: lambda)」ができてる。

f:id:trafalbad:20190518083430p:plain




4.Lambdaのデプロイ


$ apex deploy

S3やcloudwatchログが紐付いたLambda関数が出来てる。関数名は”{{.Project.Name}}_{{.Function.Name}}”。

f:id:trafalbad:20190518083510p:plain




5.残りのAWSリソース(Lambdaのトリガー)のデプロイ


$ apex infra apply

Lambda関数のトリガーにcloudwatchログが追加されてる
f:id:trafalbad:20190518083548p:plain




6.後片付け


$ apex infra destroy
$ apex delete

IAMロール & Lambda関数がキレイに破壊されてる。

f:id:trafalbad:20190518083805j:plain





4.まとめ

terraformコマンド集

# terraformインストール
$ brew install terraform

# バージョン確認
$ terraform --version
$ terraform init   

# インフラ環境を新しく作るため、変更内容を確認
$ terraform plan

# planで確認した内容が実行
$ terraform apply

# 進行状況の確認
$ terraform show

# リソースの全削除
$ terraform destroy


理解するためにやったこと



・terraformでEC2だけ作ってみる

・Lambdaにzipファイルをアップロードしてみる

GUIでCloudWatch イベントでAWS Lambda 関数をスケジュールしてみる

・サイト「Apex+Terraformでサーバレスアーキテクチャをフルコード化」を一通りやってみる



参考サイト



Apex+Terraformでサーバレスアーキテクチャをフルコード化

Providers - Terraform by HashiCorp(Terraform公式サイト)

Apex公式サイト