Courseraのdeeplearning.ai:CNN-ニューラルスタイル転送によるアート生成



Courseras Deeplearning



ディープラーニングとアート:ニューラルスタイルの転送

アルゴリズムはGatysらによって提案されています。 (2015)( https://arxiv.org/abs/1508.06576 )。
この実験では、芸術的な画像を生成するためにアルゴリズムを使用するニューラルスタイル転送アルゴリズムを実装できます。
多くのアルゴリズムは、コスト関数を最適化してパラメーター値のセットを取得します。 Neural Style Transferで、コスト関数を最適化してピクセル値を取得します。

1-問題の説明

ニューラルスタイル転送(NST)は、深層学習における興味深い手法です。以下に示すように、彼はコンテンツ画像(C)とスタイル画像(S)という名前の2つの画像をマージして、画像(G)を生成しました。 Gには、画像Cのコンテンツと画像Sのスタイルが含まれます。
この例では、パリで撮影されたルーブル美術館の写真(コンテンツ画像C)と印象派の指導者クロードモネの絵画が混合されます。
画像



2-転移学習

ニューラルスタイル転送(NST)は、事前にトレーニングされた畳み込みニューラルネットワークを使用し、その上に構築されます。さまざまなタスクでトレーニングされたネットワークを使用し、それを新しいタスクに適用するというアイデアは、転移学習と呼ばれます。
元のNSTペーパーによると( https://arxiv.org/abs/1508.06576 )、VGGネットワ​​ークを使用します。 VGGネットワ​​ークの19層バージョンであるVGG-19を使用します。このモデルは非常に大規模なImageNetデータベースでトレーニングされているため、さまざまな低レベルの機能(初期レベル)と高レベルの機能(より深いレベル)を認識することを学習しました。
次のコードを実行してVGGモデルからパラメーターをロードするには、数秒かかる場合があります。

model = load_vgg_model('pretrained-model/imagenet-vgg-verydeep-19.mat') print(model)

モデルはPythonディクショナリに格納され、各変数名はキーであり、対応する値は変数値を含むテンソルです。このネットワークで画像を実行するには、画像をモデルにフィードするだけです。 TensorFlowを使用すると、tf.assign関数を使用できます。割り当て文字を使用できます
model['input'].assign(image)
モデルの入力と同様に、ネットワークがイメージ上で実行されているときに4_2レイヤーなどの特定のレイヤーのアクティブ化にアクセスする場合は、正しいテンソルconv4_2でTensorFlowセッションを実行できます。



sess.run(model['conv4_2'])

3-ニューラルスタイル転送

NSTアルゴリズムは、次の3つのステップに分かれています。

  • コンテンツコスト関数を計算する Jcまたはntですnt((CG)。 J c o n t e n t(C、G)
  • スタイルコスト関数を計算する JstYlです((CG)。 J s t y l e(C、G)
  • 総コスト関数を計算する J((G)。=aJcまたはntですnt((CG)。+bJstYlです((CG)。 J(G)=αJco n t e n t(C、G)+βJst y l e(C、G)
    3.1-コンテンツコストを計算する
    実行中の例では、コンテンツ画像Cはパリのルーブル美術館の写真です。コードを実行して、ルーブル美術館の写真を表示します。
content_image = scipy.misc.imread('images/louvre.jpg') imshow(content_image)

画像
3.1.1-生成された画像Gが画像Cのコンテンツと一致することを確認する方法
ConvNetの初期(より軽い)レイヤーは、エッジや単純なテクスチャなどの低レベルの機能を検出する傾向があり、後期(より深い)レイヤーは、より複雑なテクスチャやオブジェクトクラスなどのより高度な機能を検出する傾向があります。
生成された画像Gには、入力画像Cと同様のコンテンツが必要です。画像のコンテンツを表すために、特定のレイヤーのアクティブ化が選択されていると想定されます。実際には、ネットワークの中央にあるレイヤーを選択すると、視覚的に最も心地よい効果が得られます。 (演習が完了したら、自由に戻ってさまざまなレイヤーを使用して、結果がどのように変化するかを確認できます)
特定の隠れ層が使用のために選択されていると想定されます。ここで、事前にトレーニングされたVGGネットワ​​ークの入力として画像Cを設定し、それを前方に伝播します。作る C 交流選択したレイヤーの非表示レイヤーアクティベーションは nH××n××nC nH×nW×nCテンソル。画像Gでこのプロセスを繰り返します。Gを入力として設定してから、先に進みます。作る G G対応する隠しレイヤーがアクティブになります。コンテンツ損失関数を次のように定義します。



Jcまたはntですnt((CG)。=14××nH××n××nCすべてのエントリ((((C)。-((G)。)。(1) (1)J c o n t e n t(C、G)= 14×nH×nW×nC ∑すべてのエントリ(a(C)− a(G))2
ここに、 nHnnC n H、n W、n C選択した非表示レイヤーの高さ、幅、数であり、標準化されたコスト項目として表示されます。 C 交流 G G一部のボリュームは、非表示レイヤーのアクティブ化に対応しています。計算用 Jcまたはntですnt((CG)。 J c o n t e n t(C、G)、以下に示すように、これらの3Dボリュームを2Dマトリックスに拡張します。 (技術的に言えば、これらの展開率を計算する必要はありません Jcまたはntですnt J c o n t e n t、ただし、後でスタイル定数を計算するために同様の操作を実行する必要がある場合 JstYlです J s t y l e、これは良い習慣です。 )。
画像
演習:TensorFlowを使用してコンテンツの損失を計算する
説明:この機能を実現するための3つのステップは次のとおりです。
1.から G小切手そう寸法程度 Gチェックソー寸法度
  • テンソルXから次元を取得するには、次を使用します。X.get_shape().as_list()
  • 上記のようにa_Cとa_Gを展開します
  • コンテンツのコストを計算します。
# GRADED FUNCTION: compute_content_cost def compute_content_cost(a_C, a_G): ''' Computes the content cost Arguments: a_C -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing content of the image C a_G -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing content of the image G Returns: J_content -- scalar that you compute using equation 1 above. ''' ### START CODE HERE ### # Retrieve dimensions from a_G (≈1 line) m, n_H, n_W, n_C = a_G.get_shape().as_list() # Reshape a_C and a_G (≈2 lines) a_C_unrolled = tf.reshape(a_C,shape=[m, n_H*n_W, n_C]) a_G_unrolled = tf.reshape(a_G,shape=[m, n_H*n_W, n_C]) # compute the cost with tensorflow (≈1 line) J_content = tf.reduce_sum(tf.square(a_C_unrolled - a_G_unrolled))/(4 * n_H * n_W * n_C) ### END CODE HERE ### return J_content tf.reset_default_graph() with tf.Session() as test: tf.set_random_seed(1) a_C = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4) a_G = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4) J_content = compute_content_cost(a_C, a_G) print('J_content = ' + str(J_content.eval()))

期待される結果は次のとおりです。J_content = 6.76559
3.2-スタイルコストを計算する
ランニングケースでは、次のスタイルの画像を使用します。

style_image = scipy.misc.imread('images/monet_800600.jpg') imshow(style_image)

画像
3.2.1スタイルのマトリックス
スタイル行列もグラム行列になります。線形代数では、ベクトルのセット ((v1vn)。 (v 1、。。。、v n)グラム行列Gは、内積の行列です。 Gj=vTvj=npdまたはt((vvj)。 G i j = v i T v j = np。 d o t(v i、v j)。言い換えると、 Gj G i j計算 v v i vj v j類似度:それらが非常に類似している場合、それらは大きな内積を持つと予想されるので、 Gj G i j大きい。
NSTでは、計算スタイルの行列は、展開されたフィルター行列とそれらの転置行列を乗算することによって計算されます:
画像
結果は行列の次元です( nCnC n C、n C)、 nC n Cフィルタの数です。 Gj G i j計算フィルター フィルタを使用したアクティベーション j jアクティベーションの類似性。
対角要素 G G i iどのようにアクティブなフィルターを測定したか です。たとえば、フィルタを想定します 画像内の垂直テクスチャを検出します。その後、 G G i i画像全体の垂直テクスチャの普及率の尺度です:if G G i i非常に大きいため、この画像には多くの垂直テクスチャがあります。
さまざまなタイプの有病率をキャプチャすることによって( G G i i)、およびいくつの異なる機能が一緒に表示されます( Gj G i j)、スタイル行列Gは画像のスタイルを測定します。
演習:TensorFlowを使用して、行列Aを計算するグラム行列を実装します。式は次のとおりです。Aのグラム行列は G=T G A = A A T

# GRADED FUNCTION: gram_matrix def gram_matrix(A): ''' Argument: A -- matrix of shape (n_C, n_H*n_W) Returns: GA -- Gram matrix of A, of shape (n_C, n_C) ''' ### START CODE HERE ### (≈1 line) GA = tf.matmul(A, tf.transpose(A)) ### END CODE HERE ### return GA tf.reset_default_graph() with tf.Session() as test: tf.set_random_seed(1) A = tf.random_normal([3, 2*1], mean=1, stddev=4) GA = gram_matrix(A) print('GA = ' + str(GA.eval()))

予想された結果:

GA = [[ 6.42230511 -4.42912197 -2.09668207] [ -4.42912197 19.46583748 19.56387138] [ -2.09668207 19.56387138 20.6864624 ]]

3.2.2スタイルの損失
グラム行列を計算した後の目標は、スタイル画像Sのグラム行列と生成された画像Gのグラム行列の間の距離を最小化することです。これで、1つの隠れ層のみが使用されます。 [l] a [l]、このレイヤーに対応するスタイルの損失は、次のように定義されます。

J[l]stYlです((SG)。=14××nC××((nH××n)。=1nCj=1nC((G((S)。j-G((G)。j)。(二) (2)Jスタイル[l](S、G)= 14×nC 2×(nH×nW)2 ∑ i = 1 n C ∑ j = 1 n C(G ij(S)− G ij (G))2
どこ、 GS G S GG G Gこれらは、ネットワーク内の特定の隠れ層の隠れ層アクティベーション計算を使用する、スタイル画像と生成された画像のグラム行列です。
演習:単一レイヤーのスタイルコストを計算します。
説明:機能を実現するには、次の3つのステップがあります。
  • 隠れ層からa_G取得ディメンションをアクティブ化します:テンソルXに従ってディメンションを取得します:X.get_shape()。as_list()
  • 上に示すように、隠れ層a_Sとs_Gをアクティブにして2Dマトリックスに展開します
  • 画像SとGのスタイル行列を計算します(前述の関数を使用)
  • スタイルロスを計算する
# GRADED FUNCTION: compute_layer_style_cost def compute_layer_style_cost(a_S, a_G): ''' Arguments: a_S -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing style of the image S a_G -- tensor of dimension (1, n_H, n_W, n_C), hidden layer activations representing style of the image G Returns: J_style_layer -- tensor representing a scalar value, style cost defined above by equation (2) ''' ### START CODE HERE ### # Retrieve dimensions from a_G (≈1 line) m, n_H, n_W, n_C = a_G.get_shape().as_list() # Reshape the images to have them of shape (n_C, n_H*n_W) (≈2 lines) a_S = tf.reshape(a_S, [n_H*n_W, n_C]) a_G = tf.reshape(a_G, [n_H*n_W, n_C]) # Computing gram_matrices for both images S and G (≈2 lines) GS = gram_matrix(tf.transpose(a_S)) GG = gram_matrix(tf.transpose(a_G)) # Computing the loss (≈1 line) J_style_layer = tf.reduce_sum(tf.square(GS-GG))/(4 * n_C**2 * (n_W*n_H)**2) ### END CODE HERE ### return J_style_layer tf.reset_default_graph() with tf.Session() as test: tf.set_random_seed(1) a_S = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4) a_G = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4) J_style_layer = compute_layer_style_cost(a_S, a_G) print('J_style_layer = ' + str(J_style_layer.eval()))

望ましい結果:J_style_layer = 9.19028
(ここで、タイトルのヒント:画像の形状を(n_C、n_H * n_W)に変更しますが、ここでは(n_H * n_W、n_C)に再形成する必要があります。そうしないと、結果が間違っています)
3.2.3スタイルの重み
スタイルが1つのレイヤーからキャプチャされたので、異なるレイヤーのスタイルコストをマージする必要があり、より良い結果が得られます。トレーニングを完了した後、自由に戻ってさまざまな重みを試して、生成された画像Gがどのように変化するかを確認できます。現時点では、これは妥当なデフォルトです。

STYLE_LAYERS = [ ('conv1_1', 0.2), ('conv2_1', 0.2), ('conv3_1', 0.2), ('conv4_1', 0.2), ('conv5_1', 0.2)]

異なるレイヤーのスタイルコストを組み合わせることができます。

JstYlです((SG)。=lλ[l]J[l]stYlです((SG)。 J s t y l e(S、G)= ∑lλ [l] J s t y l e [l](S、G)
ここに、 λ[l] λ[l]STYLE_LAYERSの値与えられた。 def compute_style_cost(model, STYLE_LAYERS): ''' Computes the overall style cost from several chosen layers Arguments: model -- our tensorflow model STYLE_LAYERS -- A python list containing: - the names of the layers we would like to extract style from - a coefficient for each of them Returns: J_style -- tensor representing a scalar value, style cost defined above by equation (2) ''' # initialize the overall style cost J_style = 0 for layer_name, coeff in STYLE_LAYERS: # Select the output tensor of the currently selected layer out = model[layer_name] # Set a_S to be the hidden layer activation from the layer we have selected, by running the session on out a_S = sess.run(out) # Set a_G to be the hidden layer activation from same layer. Here, a_G references model[layer_name] # and isn't evaluated yet. Later in the code, we'll assign the image G as the model input, so that # when we run the session, this will be the activations drawn from the appropriate layer, with G as input. a_G = out # Compute style_cost for the current layer J_style_layer = compute_layer_style_cost(a_S, a_G) # Add coeff * J_style_layer of this layer to overall style cost J_style += coeff * J_style_layer return J_style

注:上記のforループの内部ループでは、a_Gはテンソルであり、まだ評価されていません。 model_nn()でTensorFlowグラフを実行すると、反復ごとに評価および更新されます。
覚えておくべき:
画像スタイルは、非表示レイヤーによってアクティブ化されたグラム行列で表すことができます。次に、複数の異なるレイヤーからのこの表現を組み合わせて、より良い結果を得ました。これは、コンテンツ表現とはまったく対照的です。この場合、通常は非表示のレイヤーで十分です。
スタイルコストを最小化すると、画像Gを画像Sのスタイルに従わせることができます。
3.3-最適化の総コストを決定する
最後に、スタイルとコンテンツのコストを最小限に抑えるコスト関数を作成しましょう。式は次のとおりです。

J((G)。=aJcまたはntですnt((CG)。+bJstYlです((SG)。 J(G)=αJco n t e n t(C、G)+βJst y l e(S、G)
演習:コンテンツコストとスタイルコストを含む総コスト関数を実装します。 GRADED FUNCTION: total_cost def total_cost(J_content, J_style, alpha = 10, beta = 40): ''' Computes the total cost function Arguments: J_content -- content cost coded above J_style -- style cost coded above alpha -- hyperparameter weighting the importance of the content cost beta -- hyperparameter weighting the importance of the style cost Returns: J -- total cost as defined by the formula above. ''' ### START CODE HERE ### (≈1 line) J = alpha * J_content + beta * J_style ### END CODE HERE ### return J tf.reset_default_graph() with tf.Session() as test: np.random.seed(3) J_content = np.random.randn() J_style = np.random.randn() J = total_cost(J_content, J_style) print('J = ' + str(J))

期待される出力:J = 35.34667875478276
注意:
総費用はコンテンツ費用です Jcまたはntですnt((CG)。 J c o n t e n t(C、G)スタイルのコスト JstYlです((SG)。 J s t y l e(S、G)線形結合
a a b bコンテンツとスタイルの間の相対的な重みを制御するハイパーパラメータです

4-最適化問題を解く

最後に、すべてをまとめてニューラルスタイル転送を実装しましょう。
プログラムが実行する必要があることは次のとおりです。

  • インタラクティブセッションを作成する
  • コンテンツ画像を読み込む
  • スタイル画像を読み込む
  • 生成する画像をランダムに初期化します
  • VGG16モデルをロードします
  • TensorFlowグラフを実行します。
    VGGモデルを介してコンテンツ画像を実行し、コンテンツコストを計算します
    VGGモデルを介してコンテンツ画像を実行し、スタイルコストを計算します
    総費用を計算する
    オプティマイザーと学習率を定義します。
  • TensorFlowグラフを初期化し、多数の反復を実行して、各ステップで生成された画像を更新します。

以前に実現されました J((G)。 J(G)総費用。次に、TensorFlowをに設定します G G最適化する。これを行うには、プログラムはチャートをリセットし、「インタラクティブセッション」を使用する必要があります。通常のセッションとは異なり、「インタラクティブセッション」はチャートを作成するための最初のセッションとしてそれ自体をインストールします。これにより、セッションオブジェクトを参照せずに変数を実行できるため、コードが簡素化されます。

# Reset the graph tf.reset_default_graph() # Start interactive session sess = tf.InteractiveSession()

「コンテンツ」画像(ルーブル美術館の写真)を読み込んで、形を変え、標準化します。

content_image = scipy.misc.imread('images/louvre_small.jpg') content_image = reshape_and_normalize_image(content_image)

「スタイル」の画像(クロード・モネの絵)を読み込んで、形を変え、標準化します。

style_image = scipy.misc.imread('images/monet.jpg') style_image = reshape_and_normalize_image(style_image)

これで、「生成された」画像は、コンテンツ画像から作成されたノイズ画像として初期化されます。初期化によって生成された画像のピクセルは主にノイズですが、コンテンツ画像とわずかに関連しているため、「生成された」画像のコンテンツを「コンテンツ」画像のコンテンツとより迅速に一致させることができます。 (nst_utils.pyでgenerate_noise_image(…)を確認できます。ノートブックの左上隅にある[ファイル]-> [開く...]をクリックします)

generated_image = generate_noise_image(content_image) imshow(generated_image[0])

画像
VGGモデルをロードします。

model = load_vgg_model('pretrained-model/imagenet-vgg-verydeep-19.mat')

プログラムがコンテンツコストを計算するために、適切な非表示レイヤーとしてa_Cとa_Gをアクティブにします。 conv4_2レイヤーを使用してコンテンツコストを計算します。次のコードは次のとおりです。

  • VGGモデルの入力としてコンテンツ画像を指定します。
  • a_Cをテンソルとして設定して、conv4_2レイヤーの非表示レイヤーのアクティブ化を提供します。
  • a_Gをテンソルに設定して、同じレイヤーに非表示レイヤーのアクティブ化を提供します。
  • a_Cおよびa_Gを使用して、コンテンツのコストを計算します。
# Assign the content image to be the input of the VGG model. sess.run(model['input'].assign(content_image)) # Select the output tensor of layer conv4_2 out = model['conv4_2'] # Set a_C to be the hidden layer activation from the layer we have selected a_C = sess.run(out) # Set a_G to be the hidden layer activation from same layer. Here, a_G references model['conv4_2'] # and isn't evaluated yet. Later in the code, we'll assign the image G as the model input, so that # when we run the session, this will be the activations drawn from the appropriate layer, with G as input. a_G = out # Compute the content cost J_content = compute_content_cost(a_C, a_G)

注:a_Gはテンソルであり、評価されていません。 model_nn()でTensorflowグラフを実行すると、反復ごとに評価および更新されます。

# Assign the input of the model to be the 'style' image sess.run(model['input'].assign(style_image)) # Compute the style cost J_style = compute_style_cost(model, STYLE_LAYERS)

演習:使用 Jcまたはntですnt J c o n t e n t JstYlです J s t y l e、電話でtotalcost()総費用を計算するには J J、使用する lph=10 a l p h a = 10 bですt=40 b e t a = 40

### START CODE HERE ### (1 line) J = total_cost(J_content, J_style, alpha=10, beta=40) ### END CODE HERE ###

TensorFlowでAdamオプティマイザーを設定すると、学習率は2.0になります。

# define optimizer (1 line) optimizer = tf.train.AdamOptimizer(2.0) # define train_step (1 line) train_step = optimizer.minimize(J)

演習:model_nn()関数を実装します。この関数は、テンソルフローグラフの変数を初期化し、入力画像(最初に生成された画像)をVGG16モデルの入力に設定し、トレインステップステップを実行します。

def model_nn(sess, input_image, num_iterations = 200): # Initialize global variables (you need to run the session on the initializer) ### START CODE HERE ### (1 line) sess.run(tf.global_variables_initializer()) ### END CODE HERE ### # Run the noisy input image (initial generated image) through the model. Use assign(). ### START CODE HERE ### (1 line) sess.run(model['input'].assign(input_image)) ### END CODE HERE ### for i in range(num_iterations): # Run the session on the train_step to minimize the total cost ### START CODE HERE ### (1 line) sess.run(train_step) ### END CODE HERE ### # Compute the generated image by running the session on the current model['input'] ### START CODE HERE ### (1 line) generated_image = sess.run(model['input']) ### END CODE HERE ### # Print every 20 iteration. if i%20 == 0: Jt, Jc, Js = sess.run([J, J_content, J_style]) print('Iteration ' + str(i) + ' :') print('total cost = ' + str(Jt)) print('content cost = ' + str(Jc)) print('style cost = ' + str(Js)) # save current generated image in the '/output' directory save_image('output/' + str(i) + '.png', generated_image) # save last generated image save_image('output/generated_image.jpg', generated_image) return generated_image

次のユニットを実行して、芸術的な画像を生成します。 20回の反復ごとに、CPUの実行時間は約3分かかります。 140回の反復の後、魅力的な結果を観察し始めます。神経タイプの転送は通常、GPUでトレーニングされます。

model_nn(sess, generated_image)

期待される出力:
反復0:
総コスト= 5.05035e + 09
コンテンツコスト= 7877.67
スタイルコスト= 1.26257e + 08

実行後、ノートブックの上部の列にある[ファイル]、[開く]の順にクリックします。 '/ output'ディレクトリに移動して、保存されているすべての画像を表示します。 「生成された画像」を開いて、生成された画像を表示します。 :)
下の画像の右側に画像が表示されます。
画像

完全なコードリンク: deeplearning.ai/ニューラルスタイル転送によるアート生成-v2.ipynb