خلق فن باستخدام DCGAN على Keras

يوم جيد. منذ ستة أشهر ، بدأت دراسة التعلم الآلي ، وخضعت لبضع دورات واكتسبت بعض الخبرة في هذا المجال. بعد أن رأيت كل أنواع الأخبار حول الشبكات العصبية الرائعة ويمكنها فعل الكثير ، قررت أن أحاول دراستها. بدأت في قراءة كتاب نيكولنكو حول التعلم العميق ، وخلال فترة قراءتي توصلت إلى عدة أفكار (ليست جديدة بالنسبة للعالم ، ولكنها كانت تهمني كثيرًا) ، أحدها هو إنشاء شبكة عصبية من شأنها أن تولد فنًا يبدو رائعًا وليس فقط بالنسبة لي ، "والد الطفل الرسم" ، ولكن أيضا لأشخاص آخرين. في هذه المقالة ، سأحاول وصف المسار الذي سلكته من أجل الحصول على النتائج الأولى التي ترضي.


جمع البيانات


عندما قرأت الفصل على الشبكات التنافسية ، أدركت أنه يمكنني الآن كتابة شيء ما.
كانت إحدى المهام الأولى كتابة محلل صفحة ويب لجمع مجموعة البيانات. لهذا ، كان موقع wikiart مثاليًا ، ويحتوي على عدد كبير من اللوحات ويتم جمعها جميعًا حسب الأسلوب. كان هذا هو أول محلل خاص بي ، لذلك قمت بكتابته لمدة 4-5 أيام ، أول 3 منها أخذ الوخزات على طريق خاطئ تمامًا. كانت الطريقة الصحيحة هي الانتقال إلى علامة تبويب الشبكة في الكود المصدر للصفحة وتتبع كيفية ظهور الصور عند النقر فوق الزر "المزيد". في الواقع ، بالنسبة للمبتدئين مثلي ، سيكون من الجيد عرض الكود.


from scipy.misc import imresize, imsave from matplotlib.image import imread import requests import json from bs4 import BeautifulSoup from itertools import count import os import glob 

في أول خلية لكوكب المشتري ، قمت باستيراد المكتبات اللازمة.


  • glob - شيء مفيد للحصول على قائمة بالملفات في الدليل
  • طلبات ، BeautifulSoup - شارب للتحليل
  • json - مكتبة للحصول على قاموس يتم إرجاعه عند النقر فوق الزر "المزيد" على الموقع
  • تغيير حجم ، حفظ ، imread - لقراءة الصور وإعدادهم.

 def get_page(style, pagenum): page = requests.get(url1 + style + url2 + str(pagenum) + url3) return page def make_soup(page): soup = BeautifulSoup(page.text, 'html5lib') return soup def make_dir(name, s): path = os.getcwd() + '/' + s + '/' + name os.mkdir(path) 

أصف وظائف لعملية مريحة.


الأول - يحصل على صفحة في شكل نص ، والثاني يجعل هذا النص أكثر ملاءمة للعمل. حسنًا ، الثالث هو إنشاء المجلدات الضرورية حسب الأسلوب.


 styles = ['kubizm'] url1 = 'https://www.wikiart.org/ru/paintings-by-style/' url2 = '?select=featured&json=2&layout=new&page=' url3 = '&resultType=masonry' 

في مجموعة الأنماط ، حسب التصميم ، كان ينبغي أن يكون هناك العديد من الأنماط ، ولكن حدث ذلك أنني قمت بتنزيلها بشكل غير متساوٍ.


 for style in styles: make_dir(style, 'images') for style in styles: make_dir(style, 'new256_images') 

إنشاء المجلدات اللازمة. تقوم الدورة الثانية بإنشاء مجلدات سيتم تخزين الصورة بها ، وتم تسويتها في مساحة 256 × 256.


(في البداية فكرت بطريقة ما بعدم تطبيع أحجام الصور بحيث لا يكون هناك تشوهات ، لكنني أدركت أن هذا كان إما مستحيلًا أو صعبًا جدًا بالنسبة لي)


 for style in styles: path = os.getcwd() + '\\images\\' + style + '\\' images = [] names = [] titles = [] for pagenum in count(start=1): page = get_page(style, pagenum) if page.text[0]!='{': break jsons = json.loads(page.text) paintings = jsons['Paintings'] if paintings is None: break for item in paintings: images_temp = [] images_dict = item['images'] if images_dict is None: images_temp.append(item['image']) names.append(item['artistName']) titles.append(item['title']) else: for inner_item in item['images']: images_temp.append(inner_item['image']) names.append(item['artistName']) titles.append(item['title']) images.append(images_temp) for char in ['/','\\','"', '?', ':','*','|','<','>']: titles = [title.replace(char, ' ') for title in titles] for listimg, name, title in zip(images, names, titles): if len(name) > 30: name = name[:25] if len(title) > 50: title = title[:50] if len(listimg) == 1: response = requests.get(listimg[0]) if response.status_code == 200: with open(path + name + ' ' + title + '.png', 'wb') as f: f.write(response.content) else: print('Error from server') else: for i, img in enumerate(listimg): response = requests.get(img) if response.status_code == 200: with open(path + name + ' ' + title + str(i) + '.png', 'wb') as f: f.write(response.content) else: print('Error from server') 

هنا يتم تحميل الصور وحفظها في المجلد المطلوب. هنا الصور لا تتغير حجمها ، يتم حفظ النسخ الأصلية.


تحدث أشياء مثيرة للاهتمام في الحلقة المتداخلة الأولى:


قررت باستمرار أن أسأل باستمرار json's (json هو القاموس الذي يعرضه الخادم عند النقر فوق الزر "المزيد". يحتوي القاموس على جميع المعلومات حول الصور) ، والتوقف عندما يعرض الخادم شيئًا ما مضطربًا وليس مثل القيم المعتادة . في هذه الحالة ، يجب أن يكون الحرف الأول للنص الذي تم إرجاعه عبارة عن شريحة منحنية مفتوحة ، وبعد ذلك يأتي نص القاموس.


وقد لوحظ أيضًا أن الخادم يمكنه إرجاع شيء مثل ألبوم صور. هذا هو في الأساس مجموعة من اللوحات. في البداية اعتقدت أن اللوحات الفردية كانت تعود ، أو اسم الفنانين إليها ، أو ربما بحيث يتم تقديم مجموعة من اللوحات مع اسم واحد للفنان.


  for style in styles: directory = os.getcwd() + '\\images\\' + style + '\\' new_dir = os.getcwd() + '\\new256_images\\' + style + '\\' filepaths = [] for dir_, _, files in os.walk(directory): for fileName in files: #relDir = os.path.relpath(dir_, directory) #relFile = os.path.join(relDir, fileName) relFile = fileName #print(directory) #print(relFile) filepaths.append(relFile) #print(filepaths[-1]) print(filepaths[0]) for i, fp in enumerate(filepaths): img = imread(directory + fp, 0) #/ 255.0 img = imresize(img, (256, 256)) imsave(new_dir + str(i) + ".png", img) 

هنا يتم تغيير حجم الصور وحفظها في المجلد المعد لها.


حسنا ، يتم تجميع مجموعة البيانات ، يمكنك المتابعة إلى الأكثر إثارة للاهتمام!


بداية صغيرة



علاوة على ذلك ، بعد قراءة المقال الأصلي ، بدأت في خلق! ولكن ما كان خيبة أملي عندما خرج شيء جيد. في هذه المحاولات ، قمت بتدريب الشبكة على نفس النمط من الصور ، لكن حتى ذلك لم ينجح ، لذلك قررت البدء في تعلم كيفية إنشاء أرقام من المضاعف. لن أتطرق هنا بالتفصيل ، سأتحدث فقط عن الهندسة المعمارية ونقطة التحول ، وذلك بفضل الأرقام التي بدأت تتولد.


 def build_generator(): model = Sequential() model.add(Dense(128 * 7 * 7, input_dim = latent_dim)) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Reshape((7, 7, 128))) model.add(Conv2DTranspose(64, filter_size, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(32, filter_size, strides=(1, 1), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(img_channels, filter_size, strides=(2,2), padding='same')) model.add(Activation("tanh")) model.summary() return model 

  • latent_dim - مجموعة من 100 أرقام تم إنشاؤها بشكل عشوائي.


     def build_discriminator(): model = Sequential() model.add(Conv2D(64, kernel_size=filter_size, strides = (2,2), input_shape=img_shape, padding="same")) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(128, kernel_size=filter_size, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(128, kernel_size=filter_size, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Flatten()) model.add(Dense(1)) model.add(Activation('sigmoid')) model.summary() return model 

    وهذا هو ، في المجموع ، أحجام الإخراج من الطبقات التلافيفية وعدد الطبقات عموما أقل مما كانت عليه في المادة الأصلية. 28x28 لأنني تولد ، وليس الديكور الداخلي!



حسنًا ، فإن الخدعة التي يرجع سببها إلى كل شيء قد نجحت - في التكرار المتساوي للتدريب - نظر المميّز إلى الصور الناتجة ، والتكرار الغريب - إلى الصور الحقيقية.


هذا هو الأساس. تعلم DCGAN مماثل بسرعة كبيرة ، على سبيل المثال ، تم الحصول على الصورة في بداية هذا الموضوع الفرعي في العصر 19 ،



هذه بالفعل واثقة ، لكن في بعض الأحيان ليست أرقامًا حقيقية ، فقد تحولت إلى حقبة التعليم التاسعة والتسعين.


راضية عن النتيجة الأولية ، توقفت عن التعلم وبدأت في التفكير في كيفية حل المشكلة الرئيسية.


شبكة الخصومة الإبداعية


كانت الخطوة التالية هي قراءة حول GAN مع التسميات: يتم تقديم فئة الصورة الحالية إلى التمييز والمولد. وبعد أن حصلت على بطاقة تعريفية ، تعرفت على CAN - فك التشفير هو في الأساس اسم الموضوع الفرعي.


في CAN ، يحاول المُميّز تخمين فئة الصورة إذا كانت الصورة من مجموعة حقيقية. ووفقًا لذلك ، في حالة التدريب في صورة حقيقية ، كخطأ ، يتم تمييز خطأً عن المخالف ، بالإضافة إلى الافتراضي ، من تخمين الفصل.


عند التدريب على صورة تم إنشاؤها ، يحتاج المُميّز فقط إلى التنبؤ بما إذا كانت هذه الصورة حقيقية أم لا.


المولد ، بالإضافة إلى ذلك ، لمجرد خداع المميّز ، يحتاج إلى جعل المميّز في حيرة عند تخمين فئة الصورة ، أي أن المولد سيكون مهتمًا بحقيقة أن المخرجات للمُميِّزين تكون بقدر الإمكان من الثقة الكاملة.


أنتقل مرة أخرى إلى CAN ، لقد عانيت مرة أخرى من صعوبات وفساد بسبب حقيقة أن لا شيء يعمل ولا يتعلم. بعد العديد من الإخفاقات غير السارة ، قررت البدء من جديد وحفظ جميع التغييرات (نعم ، لم أفعل ذلك من قبل) والأوزان والهندسة المعمارية (لمقاطعة التدريب).


أولاً ، أردت أن أقوم بإنشاء شبكة من شأنها أن تنشئ صورة 256 × 256 واحدة بالنسبة لي (جميع الصور التالية بهذا الحجم) دون أي تسميات. كانت نقطة الانطلاق هنا هي ، على العكس من ذلك ، في كل تكرار من التدريب ، ينبغي إعطاء المتمييز نظرة على الصور المولدة والصور الحقيقية.



هذه هي النتيجة التي توقفت عندها وانتقلت إلى الخطوة التالية. نعم ، تختلف الألوان عن الصورة الحقيقية ، لكنني كنت مهتمًا بشكل أكبر بقدرة الشبكة على إبراز المعالم والكائنات. لقد تعاملت مع هذا.


ثم يمكننا المضي قدما في المهمة الرئيسية - توليد الفن. قدم الكود على الفور ، والتعليق عليه على طول الطريق.


أولاً ، كما هو الحال دائمًا ، تحتاج إلى استيراد جميع المكتبات.


 import glob from PIL import Image from keras.preprocessing.image import array_to_img, img_to_array, load_img from datetime import date from datetime import datetime import tensorflow as tf import numpy as np import argparse import math import os from matplotlib.image import imread from scipy.misc.pilutil import imresize, imsave import matplotlib.pyplot as plt import cv2 import keras from keras.models import Sequential, Model from keras.layers import Dense, Activation, Reshape, Flatten, Dropout, Input from keras.layers.convolutional import Conv2D, Conv2DTranspose, MaxPooling2D from keras.layers.normalization import BatchNormalization from keras.layers.advanced_activations import LeakyReLU from keras.optimizers import Adam, SGD from keras.datasets import mnist from keras import initializers import numpy as np import random 

إنشاء مولد.


يختلف إخراج الطبقات مرة أخرى عن المقالة. في مكان ما من أجل حفظ الذاكرة (الشروط: كمبيوتر منزلي مع gtx970) ، وفي مكان ما بسبب النجاح مع التكوين


 def build_generator(): model = Sequential() model.add(Dense(128 * 16 * 8, input_dim = latent_dim)) model.add(BatchNormalization()) model.add(LeakyReLU()) model.add(Reshape((8, 8, 256))) model.add(Conv2DTranspose(512, filter_size_g, strides=(1,1), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(512, filter_size_g, strides=(1,1), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(256, filter_size_g, strides=(1,1), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(128, filter_size_g, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(64, filter_size_g, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(32, filter_size_g, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(16, filter_size_g, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(8, filter_size_g, strides=(2,2), padding='same')) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU()) model.add(Conv2DTranspose(img_channels, filter_size_g, strides=(1,1), padding='same')) model.add(Activation("tanh")) model.summary() return model 

تُرجع دالة إنشاء تمييز تمييز نموذجين ، أحدهما يحاول معرفة ما إذا كانت الصورة حقيقية ، والآخر يحاول معرفة فئة الصورة.


 def build_discriminator(num_classes): model = Sequential() model.add(Conv2D(64, kernel_size=filter_size_d, strides = (2,2), input_shape=img_shape, padding="same")) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(128, kernel_size=filter_size_d, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(256, kernel_size=filter_size_d, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(512, kernel_size=filter_size_d, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(512, kernel_size=filter_size_d, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Conv2D(512, kernel_size=filter_size_d, strides = (2,2), padding="same")) model.add(BatchNormalization(momentum=0.8)) model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25)) model.add(Flatten()) model.summary() img = Input(shape=img_shape) features = model(img) validity = Dense(1)(features) valid = Activation('sigmoid')(validity) label1 = Dense(1024)(features) lrelu1 = LeakyReLU(alpha=0.2)(label1) label2 = Dense(512)(label1) lrelu2 = LeakyReLU(alpha=0.2)(label2) label3 = Dense(num_classes)(label2) label = Activation('softmax')(label3) return Model(img, valid), Model(img, label) 

وظيفة لخلق نموذج تنافسي. في نموذج تنافسي ، لا يتم تدريب المتمييز.


 def generator_containing_discriminator(g, d, d_label): noise = Input(shape=(latent_dim,)) img = g(noise) d.trainable = False d_label.trainable = False valid, target_label = d(img), d_label(img) return Model(noise, [valid, target_label]) 

وظيفة لتنزيل دفعة مع الصور الحقيقية والتسميات. البيانات - مجموعة من العناوين التي سيتم تحديدها في وقت لاحق. في نفس الوظيفة ، يتم تطبيع الصورة.


 def get_images_classes(batch_size, data): X_train = np.zeros((batch_size, img_rows, img_cols, img_channels)) y_labels = np.zeros(batch_size) choice_arr = np.random.randint(0, len(data), batch_size) for i in range(batch_size): rand_number = np.random.randint(0, len(data[choice_arr[i]])) temp_img = cv2.imread(data[choice_arr[i]][rand_number]) X_train[i] = temp_img y_labels[i] = choice_arr[i] X_train = (X_train - 127.5)/127.5 return X_train, y_labels 

وظيفة لإخراج جميلة من دفعة الصورة. في الواقع ، تم جمع كل الصور من هذه المقالة عن طريق هذه الوظيفة.


 def combine_images(generated_images): num = generated_images.shape[0] width = int(math.sqrt(num)) height = int(math.ceil(float(num)/width)) shape = generated_images.shape[1:3] image = np.zeros((height*shape[0], width*shape[1], img_channels), dtype=generated_images.dtype) for index, img in enumerate(generated_images): i = int(index/width) j = index % width image[i*shape[0]:(i+1)*shape[0], j*shape[1]:(j+1)*shape[1]] = \ img[:, :, :,] return image 

وهنا هذه البيانات نفسها. في شكل أكثر أو أقل ملاءمة إرجاع مجموعة من عناوين الصور ، والتي ، نحن أعلاه ، مرتبة في مجلدات


 def get_data(): styles_folder = os.listdir(path=os.getcwd() + "\\new256_images\\") num_styles = len(styles_folder) data = [] for i in range(num_styles): data.append(glob.glob(os.getcwd() + '\\new256_images\\' + styles_folder[i] + '\\*')) return data, num_styles 

لتمرير العصر ، تم تعيين عدد كبير عشوائي ، لأنه كان كسولًا جدًا لحساب عدد جميع الصور. في نفس الوظيفة يتم توفير تحميل الموازين إذا كان من الضروري مواصلة التدريب. كل 5 عصور ، يتم الحفاظ على الوزن والعمارة.


من الجدير أيضًا بالكتابة أنني حاولت إضافة ضوضاء على الصور المدخلة ، لكن في التدريب الأخير قررت عدم القيام بذلك.
يتم استخدام الملصقات الصفية الملساء ، فهي تساعد كثيرا في التعلم.


 def train_another(epochs = 100, BATCH_SIZE = 4, weights = False, month_day = '', epoch = ''): data, num_styles = get_data() generator = build_generator() discriminator, d_label = build_discriminator(num_styles) discriminator.compile(loss=losses[0], optimizer=d_optim) d_label.compile(loss=losses[1], optimizer=d_optim) generator.compile(loss='binary_crossentropy', optimizer=g_optim) if month_day != '': generator.load_weights(os.getcwd() + '/' + month_day + epoch + ' gen_weights.h5') discriminator.load_weights(os.getcwd() + '/' + month_day + epoch + ' dis_weights.h5') d_label.load_weights(os.getcwd() + '/' + month_day + epoch + ' dis_label_weights.h5') dcgan = generator_containing_discriminator(generator, discriminator, d_label) dcgan.compile(loss=losses[0], optimizer=g_optim) discriminator.trainable = True d_label.trainable = True for epoch in range(epochs): for index in range(int(15000/BATCH_SIZE)): noise = np.random.normal(0, 1, (BATCH_SIZE, latent_dim)) real_images, real_labels = get_images_classes(BATCH_SIZE, data) #real_images += np.random.normal(size = img_shape, scale= 0.1) generated_images = generator.predict(noise) X = real_images real_labels = real_labels - 0.1 + np.random.rand(BATCH_SIZE)*0.2 y_classif = keras.utils.to_categorical(np.zeros(BATCH_SIZE) + real_labels, num_styles) y = 0.8 + np.random.rand(BATCH_SIZE)*0.2 d_loss = [] d_loss.append(discriminator.train_on_batch(X, y)) discriminator.trainable = False d_loss.append(d_label.train_on_batch(X, y_classif)) print("epoch %d batch %d d_loss : %f, label_loss: %f" % (epoch, index, d_loss[0], d_loss[1])) X = generated_images y = np.random.rand(BATCH_SIZE) * 0.2 d_loss = discriminator.train_on_batch(X, y) print("epoch %d batch %d d_loss : %f" % (epoch, index, d_loss)) noise = np.random.normal(0, 1, (BATCH_SIZE, latent_dim)) discriminator.trainable = False d_label.trainable = False y_classif = keras.utils.to_categorical(np.zeros(BATCH_SIZE) + 1/num_styles, num_styles) y = np.random.rand(BATCH_SIZE) * 0.3 g_loss = dcgan.train_on_batch(noise, [y, y_classif]) d_label.trainable = True discriminator.trainable = True print("epoch %d batch %d g_loss : %f, label_loss: %f" % (epoch, index, g_loss[0], g_loss[1])) if index % 50 == 0: image = combine_images(generated_images) image = image*127.5+127.5 cv2.imwrite( os.getcwd() + '\\generated\\epoch%d_%d.png' % (epoch, index), image) image = combine_images(real_images) image = image*127.5+127.5 cv2.imwrite( os.getcwd() + '\\generated\\epoch%d_%d_data.png' % (epoch, index), image) if epoch % 5 == 0: date_today = date.today() month, day = date_today.month, date_today.day #      json d_json = discriminator.to_json() #     json_file = open(os.getcwd() + "/%d.%d dis_model.json" % (day, month), "w") json_file.write(d_json) json_file.close() #      json d_l_json = d_label.to_json() #     json_file = open(os.getcwd() + "/%d.%d dis_label_model.json" % (day, month), "w") json_file.write(d_l_json) json_file.close() #      json gen_json = generator.to_json() #     json_file = open(os.getcwd() + "/%d.%d gen_model.json" % (day, month), "w") json_file.write(gen_json) json_file.close() discriminator.save_weights(os.getcwd() + '/%d.%d %d_epoch dis_weights.h5' % (day, month, epoch)) d_label.save_weights(os.getcwd() + '/%d.%d %d_epoch dis_label_weights.h5' % (day, month, epoch)) generator.save_weights(os.getcwd() + '/%d.%d %d_epoch gen_weights.h5' % (day, month, epoch)) 

تهيئة المتغيرات وتشغيل التدريب. نظرًا لانخفاض "طاقة" جهاز الكمبيوتر الخاص بي ، يمكن التدريب على 16 صورة بحد أقصى.


 img_rows = 256 img_cols = 256 img_channels = 3 img_shape = (img_rows, img_cols, img_channels) latent_dim = 100 filter_size_g = (5,5) filter_size_d = (5,5) d_strides = (2,2) color_mode = 'rgb' losses = ['binary_crossentropy', 'categorical_crossentropy'] g_optim = Adam(0.0002, beta_2 = 0.5) d_optim = Adam(0.0002, beta_2 = 0.5) train_another(1000, 16) 

بشكل عام ، منذ فترة طويلة أرغب في كتابة منشور على habr حول هذه الفكرة الخاصة بي ، الآن ليس أفضل وقت لذلك ، لأن هذه الخلية العصبية كانت تدرس لمدة ثلاثة أيام وهي الآن في الحقبة 113 ، لكنني وجدت اليوم صورًا مثيرة للاهتمام ، لذلك قررت أن الوقت قد حان سوف يكتب بالفعل وظيفة!



هذه هي الصور التي خرجت اليوم. ربما بتسميةهم ، أستطيع أن أنقل للقارئ تصوري الشخصي لهذه الصور. من الملاحظ أن الشبكة ليست مدربة بما فيه الكفاية (أو قد لا يتم تدريبها بهذه الطرق على الإطلاق) ، خاصة بالنظر إلى أن الصور تم التقاطها عن طريق الكتابة ، لكنني حصلت اليوم على نتيجة أعجبتني.


تتضمن الخطط المستقبلية إعادة تدريب هذا التكوين حتى يصبح واضحًا قدرته. من المخطط أيضًا إنشاء شبكة من شأنها تكبير هذه الصور إلى أحجام معقولة. لقد تم اختراع هذا بالفعل وهناك تطبيقات.


سأكون سعيدًا جدًا للنقد البناء والنصيحة الجيدة والأسئلة.

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


All Articles