ロボットにピザを調理するように教えたす。 パヌト2ニュヌラルネットワヌクの䞀臎


内容



最埌の郚分では、ドヌドヌピザのWebサむトを解析し、成分に関するデヌタをダりンロヌドしたした。最も重芁なのは、ピザの写真です。 合蚈20のピザを自由に䜿甚できたした。 もちろん、わずか20枚の画像からトレヌニングデヌタを生成するこずはできたせん。 ただし、ピザの軞察称を䜿甚できたす。写真を1床ず぀回転させ、垂盎に反射させるこずにより、1枚の写真を720枚の画像のセットに倉換できたす。 たた、十分ではありたせんが、それでも詊しおみおください。


条件付き倉分オヌト゚ンコヌダヌをトレヌニングしおから、それが䜕であるか-生成的敵察ネットワヌク-に進みたしょう。


CVAE-条件付き倉分オヌト゚ンコヌダヌ


自動゚ンコヌダヌの手続きに぀いおは、次の優れた蚘事が圹立ちたす。



読むこずを匷くお勧めしたす。
ここでポむントに盎行したす。


CVAEずVAEの違いは、゚ンコヌダヌずデコヌダヌの䞡方を入力し、さらに別のラベルを提䟛する必芁があるこずです。 この堎合、ラベルはOneHotEncoderから受け取ったレシピのベクトルになりたす。


しかし、ニュアンスがありたす-そしお、どの時点でラベルを提出するのが意味がありたすか


私は2぀の方法を詊したした


  1. 最埌に-すべおの畳み蟌みの埌-完党に接続されたレむダヌの前
  2. 最初-最初の畳み蟌みの埌-远加のチャネルずしお远加されたす

原則ずしお、どちらの方法にも存圚する暩利がありたす。 ラベルを最埌に远加するず、画像の高レベルの機胜にラベルが远加されるのは論理的なようです。 たた、その逆-最初に远加するず、䜎レベルの機胜に関連付けられたす。 䞡方の方法を比范しおみたしょう。


レシピは最倧9぀の材料で構成されおいるこずを思い出しおください。 28個ありたすが、レシピコヌドは9x29マトリックスであり、これを拡匵するず261次元のベクトルが埗られるこずがわかりたす。


32x32のサむズの画像の堎合、512に等しい隠しスペヌスのサむズを遞択したす。
より少ない数を遞択するこずもできたすが、埌で芋られるように、これはよりがやけた結果に぀ながりたす。


ラベルを远加する最初の方法を䜿甚した゚ンコヌダヌのコヌドは、すべおの畳み蟌みの埌です。


def create_conv_cvae(channels, height, width, code_h, code_w): input_img = Input(shape=(channels, height, width)) input_code = Input(shape=(code_h, code_w)) flatten_code = Flatten()(input_code) latent_dim = 512 m_height, m_width = int(height/4), int(width/4) x = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Conv2D(16, (3, 3), activation='relu', padding='same')(x) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) flatten_img_features = Flatten()(x) x = concatenate([flatten_img_features, flatten_code]) x = Dense(1024, activation='relu')(x) z_mean = Dense(latent_dim)(x) z_log_var = Dense(latent_dim)(x) 

ラベルを远加する2番目の方法最初の畳み蟌みの埌を远加チャネルずしお䜿甚する゚ンコヌダヌのコヌド


 def create_conv_cvae2(channels, height, width, code_h, code_w): input_img = Input(shape=(channels, height, width)) input_code = Input(shape=(code_h, code_w)) flatten_code = Flatten()(input_code) latent_dim = 512 m_height, m_width = int(height/4), int(width/4) def add_units_to_conv2d(conv2, units): dim1 = K.int_shape(conv2)[2] dim2 = K.int_shape(conv2)[3] dimc = K.int_shape(units)[1] repeat_n = dim1*dim2 count = int( dim1*dim2 / dimc) units_repeat = RepeatVector(count+1)(units) #print('K.int_shape(units_repeat): ', K.int_shape(units_repeat)) units_repeat = Flatten()(units_repeat) # cut only needed lehgth of code units_repeat = Lambda(lambda x: x[:,:dim1*dim2], output_shape=(dim1*dim2,))(units_repeat) units_repeat = Reshape((1, dim1, dim2))(units_repeat) return concatenate([conv2, units_repeat], axis=1) x = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img) x = add_units_to_conv2d(x, flatten_code) #print('K.int_shape(x): ', K.int_shape(x)) # size here: (17, 32, 32) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Conv2D(16, (3, 3), activation='relu', padding='same')(x) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Flatten()(x) x = Dense(1024, activation='relu')(x) z_mean = Dense(latent_dim)(x) z_log_var = Dense(latent_dim)(x) 

どちらの堎合のデコヌダコヌドも同じです。ラベルは最初に远加されたす。


  z = Input(shape=(latent_dim, )) input_code_d = Input(shape=(code_h, code_w)) flatten_code_d = Flatten()(input_code_d) x = concatenate([z, flatten_code_d]) x = Dense(1024)(x) x = Dense(16*m_height*m_width)(x) x = Reshape((16, m_height, m_width))(x) x = Conv2D(16, (3, 3), activation='relu', padding='same')(x) x = UpSampling2D((2, 2))(x) x = Conv2D(32, (3, 3), activation='relu', padding='same')(x) x = UpSampling2D((2, 2))(x) decoded = Conv2D(channels, (3, 3), activation='sigmoid', padding='same')(x) 

ネットワヌクパラメヌタの数


  1. 4'221'987
  2. 3'954'867

1぀の時代の孊習速床


  1. 60秒
  2. 63秒

研究の40時代埌の結果


  1. 損倱-0.3232 val_loss-0.3164
  2. 損倱-0.3245 val_loss-0.3191

ご芧のずおり、2番目の方法では、ANNのメモリが少なくお枈み、より良い結果が埗られたすが、トレヌニングには少し時間がかかりたす。


結果を芖芚的に比范するこずは残っおいたす。


  1. 元の画像32x32
  2. 䜜業の結果は最初のメ゜ッドですlatent_dim = 64
  3. 結果は最初のメ゜ッドですlatent_dim = 512
  4. 䜜業の結果は2番目の方法ですlatent_dim = 512





ここで、ピザが元のレシピで゚ンコヌドされ、別のレシピでデコヌドされる堎合、ピザのスタむル転送のアプリケヌションがどのように芋えるかを芋おみたしょう。


 i = 0 for label in labels: i += 1 lbls = [] for j in range(batch_size): lbls.append(label) lbls = np.array(lbls, dtype=np.float32) print(i, lbls.shape) stt_imgs = stt.predict([orig_images, orig_labels, lbls], batch_size=batch_size) save_images(stt_imgs, dst='temp/cvae_stt', comment='_'+str(i)) 

スタむル転送の結果2番目の゚ンコヌド方法



GAN-生成的敵察ネットワヌク


そのようなネットワヌクの確立されたロシア語の名前を芋぀けるこずができたせんでした。
オプション



私はそれが奜きです



䞀連の優れた蚘事がGANの仕事の理論に圹立ちたす。



より深く理解するために-ODSの最新ブログ蚘事 ニュヌラルネットワヌクシミュレヌションゲヌム


しかし、生成ニュヌラルネットワヌクを理解し、独立しお実装しようずするず、いく぀かの困難に盎面したした。 䟋えば、発電機が真にサむケデリックな写真を䜜成するこずがありたした。


実装の理解に圹立぀さたざたな䟋


ケラスのMNIST生成的敵察モデル  mnist_gan.py 、


DCGAN Deep Convolutional GANに関するFacebook調査からの2015幎末の蚘事からのアヌキテクチャの掚奚事項


深い畳み蟌みの生成的敵察ネットワヌクによる教垫なし衚珟孊習


GANを機胜させるための䞀連の掚奚事項


GANをトレヌニングする方法は GANを機胜させるためのヒントずコツ 。


GANデザむン


 def make_trainable(net, val): net.trainable = val for l in net.layers: l.trainable = val def create_gan(channels, height, width): input_img = Input(shape=(channels, height, width)) m_height, m_width = int(height/8), int(width/8) # generator z = Input(shape=(latent_dim, )) x = Dense(256*m_height*m_width)(z) #x = BatchNormalization()(x) x = Activation('relu')(x) #x = Dropout(0.3)(x) x = Reshape((256, m_height, m_width))(x) x = Conv2DTranspose(256, kernel_size=(5, 5), strides=(2, 2), padding='same', activation='relu')(x) x = Conv2DTranspose(128, kernel_size=(5, 5), strides=(2, 2), padding='same', activation='relu')(x) x = Conv2DTranspose(64, kernel_size=(5, 5), strides=(2, 2), padding='same', activation='relu')(x) x = Conv2D(channels, (5, 5), padding='same')(x) g = Activation('tanh')(x) generator = Model(z, g, name='Generator') # discriminator x = Conv2D(128, (5, 5), padding='same')(input_img) #x = BatchNormalization()(x) x = LeakyReLU()(x) #x = Dropout(0.3)(x) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Conv2D(256, (5, 5), padding='same')(x) x = LeakyReLU()(x) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Conv2D(512, (5, 5), padding='same')(x) x = LeakyReLU()(x) x = MaxPooling2D(pool_size=(2, 2), padding='same')(x) x = Flatten()(x) x = Dense(2048)(x) x = LeakyReLU()(x) x = Dense(1)(x) d = Activation('sigmoid')(x) discriminator = Model(input_img, d, name='Discriminator') gan = Sequential() gan.add(generator) make_trainable(discriminator, False) #discriminator.trainable = False gan.add(discriminator) return generator, discriminator, gan gan_gen, gan_ds, gan = create_gan(channels, height, width) gan_gen.summary() gan_ds.summary() gan.summary() opt = Adam(lr=1e-3) gopt = Adam(lr=1e-4) dopt = Adam(lr=1e-4) gan_gen.compile(loss='binary_crossentropy', optimizer=gopt) gan.compile(loss='binary_crossentropy', optimizer=opt) make_trainable(gan_ds, True) gan_ds.compile(loss='binary_crossentropy', optimizer=dopt) 

ご芧のずおり、匁別噚は以䞋を生成する通垞のバむナリ分類噚です。


1-実際の写真の堎合、
0-停物。


トレヌニング手順



 for epoch in range(epochs): print('Epoch {} from {} ...'.format(epoch, epochs)) n = x_train.shape[0] image_batch = x_train[np.random.randint(0, n, size=batch_size),:,:,:] noise_gen = np.random.uniform(-1, 1, size=[batch_size, latent_dim]) generated_images = gan_gen.predict(noise_gen, batch_size=batch_size) if epoch % 10 == 0: print('Save gens ...') save_images(generated_images) gan_gen.save_weights('temp/gan_gen_weights_'+str(height)+'.h5', True) gan_ds.save_weights('temp/gan_ds_weights_'+str(height)+'.h5', True) # save loss df = pd.DataFrame( {'d_loss': d_loss, 'g_loss': g_loss} ) df.to_csv('temp/gan_loss.csv', index=False) x_train2 = np.concatenate( (image_batch, generated_images) ) y_tr2 = np.zeros( [2*batch_size, 1] ) y_tr2[:batch_size] = 1 d_history = gan_ds.train_on_batch(x_train2, y_tr2) print('d:', d_history) d_loss.append( d_history ) noise_gen = np.random.uniform(-1, 1, size=[batch_size, latent_dim]) g_history = gan.train_on_batch(noise_gen, np.ones([batch_size, 1])) print('g:', g_history) g_loss.append( g_history ) 

バリ゚ヌション自動゚ンコヌダヌずは異なり、ゞェネレヌタヌのトレヌニングには実画像は䜿甚されず、匁別噚ラベルのみが䜿甚されるこずに泚意しおください。 すなわち 発生噚は、匁別噚からの誀差募配で蚓緎されたす。


最も興味深いのは、敵察的なネットワヌクずいう名前はいい蚀葉ではないずいうこずです。圌らは本圓に競争しおおり、匁別噚ず発電機の損倱の枬定倀を远跡するのも楜しいです。


損倱曲線を芋るず、匁別噚はゞェネレヌタヌによっお生成された元のゎミず実際の画像を区別するこずをすぐに孊習したすが、曲線は振動し始めたす-ゞェネレヌタヌは、たすたす適切な画像を生成するこずを孊習したす。



1぀のピザリストの最初のピザはダブルペパロニのゞェネレヌタヌ32x32の孊習プロセスを瀺すgif



予想どおり、GANの結果は、倉分゚ンコヌダヌず比范しお、より鮮明な画像を提䟛したす。


CVAE + GAN-条件付き倉分オヌト゚ンコヌダヌおよび生成的敵察ネットワヌク


CVAEずGANを組み合わせお、䞡方のネットワヌクを最倧限に掻甚したす。 ナニオンの基本はシンプルなアむデアです-VAEデコヌダヌはGANゞェネレヌタヌずたったく同じ機胜を実行したすが、異なる方法で実行および孊習したす。


このすべおを䞀緒に機胜させる方法が完党に明確ではなかったずいう事実に加えお、Kerasでさたざたな損倱関数をどのように䜿甚できるかに぀いおも明確ではありたせんでした。 この問題の怜玢は、githubの䟋によっお助けられたした。


→ Keras VAEおよびGAN


そのため、Kerasのさたざたな損倱関数のアプリケヌションは、独自のレむダヌ 独自のKerasレむダヌを蚘述するをcallメ゜ッドに远加するこずで実装できたす.callメ゜ッドでは、必芁な蚈算ロゞックをadd_lossメ゜ッドぞの埌続の呌び出しで実装できたす。


䟋


 class DiscriminatorLossLayer(Layer): __name__ = 'discriminator_loss_layer' def __init__(self, **kwargs): self.is_placeholder = True super(DiscriminatorLossLayer, self).__init__(**kwargs) def lossfun(self, y_real, y_fake_f, y_fake_p): y_pos = K.ones_like(y_real) y_neg = K.zeros_like(y_real) loss_real = keras.metrics.binary_crossentropy(y_pos, y_real) loss_fake_f = keras.metrics.binary_crossentropy(y_neg, y_fake_f) loss_fake_p = keras.metrics.binary_crossentropy(y_neg, y_fake_p) return K.mean(loss_real + loss_fake_f + loss_fake_p) def call(self, inputs): y_real = inputs[0] y_fake_f = inputs[1] y_fake_p = inputs[2] loss = self.lossfun(y_real, y_fake_f, y_fake_p) self.add_loss(loss, inputs=inputs) return y_real 

孊習プロセス64x64を瀺すgif



スタむル移転䜜業の結果



そしお今、楜しい郚分です


実際には、それがすべおだったもののために-遞択した食材のためのピザの䞖代。


1぀の成分からなるレシピ぀たり、1〜27のコヌドでピザを芋おみたしょう。



予想されるように-最も人気のある成分24、20、17トマト、ペパロニ、モッツァレラを含むピザのみが倚かれ少なかれ芋える-他のすべおのオプションは、䞞い圢ずあいたいな灰色の斑点があり、必芁な堎合のみあなたは䜕かを掚枬しようずするこずができたす。


おわりに


䞀般に、実隓は郚分的に成功したずみなすこずができたす。 しかし、このようなおもちゃの䟋でも、「デヌタは新しいオむルです」ずいう哀pathの衚珟には、特に機械孊習に関しお存圚する暩利があるず感じるこずができたす。
結局のずころ、機械孊習に基づくアプリケヌションの品質は、䞻にデヌタの品質ず量に䟝存したす。


ゞェネレヌティブネットワヌクは非垞に興味深いものであり、近い将来、それらのアプリケヌションの倚くの異なる䟋が芋られるず思いたす。


ずころで、写真に察する暩利がその䜜成者のものである堎合、ニュヌラルネットワヌクが䜜成する画像に察する暩利は誰が所有しおいるのでしょうか


ご枅聎ありがずうございたした


NB。 この蚘事を曞いおいるずき、ピザは1぀もヒットしおいたせん。


参照資料




Source: https://habr.com/ru/post/J337398/


All Articles