तंत्रिका शैली स्थानांतरण

TensorFlow.org पर देखें Google Colab में चलाएं गिटहब पर देखें नोटबुक डाउनलोड करें टीएफ हब मॉडल देखें

यह ट्यूटोरियल एक छवि को दूसरी छवि की शैली में बनाने के लिए गहन शिक्षा का उपयोग करता है (क्या आप कभी पिकासो या वैन गॉग की तरह पेंट कर सकते हैं?) इसे न्यूरल स्टाइल ट्रांसफर के रूप में जाना जाता है और तकनीक को ए न्यूरल एल्गोरिथम ऑफ़ आर्टिस्टिक स्टाइल (गैटिस एट अल।) में उल्लिखित किया गया है।

शैली हस्तांतरण के एक सरल अनुप्रयोग के लिए इस ट्यूटोरियल को देखें कि कैसे TensorFlow हब से पूर्व- प्रशिक्षित मनमाना छवि शैलीकरण मॉडल का उपयोग करें या TensorFlow Lite के साथ शैली स्थानांतरण मॉडल का उपयोग कैसे करें।

तंत्रिका शैली स्थानांतरण एक अनुकूलन तकनीक है जिसका उपयोग दो छवियों को लेने के लिए किया जाता है - एक सामग्री छवि और एक शैली संदर्भ छवि (जैसे कि एक प्रसिद्ध चित्रकार द्वारा एक कलाकृति) - और उन्हें एक साथ मिलाएं ताकि आउटपुट छवि सामग्री छवि की तरह दिखे, लेकिन "चित्रित" शैली संदर्भ छवि की शैली में।

यह सामग्री छवि के सामग्री आंकड़ों और शैली संदर्भ छवि के शैली आंकड़ों से मेल खाने के लिए आउटपुट छवि को अनुकूलित करके कार्यान्वित किया जाता है। इन आँकड़ों को एक दृढ़ नेटवर्क का उपयोग करके छवियों से निकाला जाता है।

उदाहरण के लिए, आइए इस कुत्ते और वासिली कैंडिंस्की की रचना 7 की एक छवि लें:

एल्फ द्वारा विकिमीडिया कॉमन्स से येलो लैब्राडोर लुकिंग । लाइसेंस सीसी बाय-एसए 3.0

अब यह कैसा लगेगा यदि कैंडिंस्की ने इस कुत्ते की तस्वीर को विशेष रूप से इस शैली के साथ चित्रित करने का फैसला किया है? कुछ इस तरह?

सेट अप

मॉड्यूल आयात और कॉन्फ़िगर करें

import os
import tensorflow as tf
# Load compressed models from tensorflow_hub
os.environ['TFHUB_MODEL_LOAD_FORMAT'] = 'COMPRESSED'
import IPython.display as display

import matplotlib.pyplot as plt
import matplotlib as mpl
mpl.rcParams['figure.figsize'] = (12, 12)
mpl.rcParams['axes.grid'] = False

import numpy as np
import PIL.Image
import time
import functools
def tensor_to_image(tensor):
  tensor = tensor*255
  tensor = np.array(tensor, dtype=np.uint8)
  if np.ndim(tensor)>3:
    assert tensor.shape[0] == 1
    tensor = tensor[0]
  return PIL.Image.fromarray(tensor)

चित्र डाउनलोड करें और एक शैली छवि और एक सामग्री छवि चुनें:

content_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
style_path = tf.keras.utils.get_file('kandinsky5.jpg','https://storage.googleapis.com/download.tensorflow.org/example_images/Vassily_Kandinsky%2C_1913_-_Composition_7.jpg')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/example_images/Vassily_Kandinsky%2C_1913_-_Composition_7.jpg
196608/195196 [==============================] - 0s 0us/step
204800/195196 [===============================] - 0s 0us/step

इनपुट की कल्पना करें

एक छवि लोड करने के लिए एक फ़ंक्शन को परिभाषित करें और इसके अधिकतम आयाम को 512 पिक्सेल तक सीमित करें।

def load_img(path_to_img):
  max_dim = 512
  img = tf.io.read_file(path_to_img)
  img = tf.image.decode_image(img, channels=3)
  img = tf.image.convert_image_dtype(img, tf.float32)

  shape = tf.cast(tf.shape(img)[:-1], tf.float32)
  long_dim = max(shape)
  scale = max_dim / long_dim

  new_shape = tf.cast(shape * scale, tf.int32)

  img = tf.image.resize(img, new_shape)
  img = img[tf.newaxis, :]
  return img

एक छवि प्रदर्शित करने के लिए एक सरल कार्य बनाएँ:

def imshow(image, title=None):
  if len(image.shape) > 3:
    image = tf.squeeze(image, axis=0)

  plt.imshow(image)
  if title:
    plt.title(title)
content_image = load_img(content_path)
style_image = load_img(style_path)

plt.subplot(1, 2, 1)
imshow(content_image, 'Content Image')

plt.subplot(1, 2, 2)
imshow(style_image, 'Style Image')

पीएनजी

TF-Hub का उपयोग करके फास्ट स्टाइल ट्रांसफर

यह ट्यूटोरियल मूल स्टाइल-ट्रांसफर एल्गोरिथम को प्रदर्शित करता है, जो छवि सामग्री को एक विशेष शैली में अनुकूलित करता है। विवरण में जाने से पहले, आइए देखें कि TensorFlow हब मॉडल यह कैसे करता है:

import tensorflow_hub as hub
hub_model = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2')
stylized_image = hub_model(tf.constant(content_image), tf.constant(style_image))[0]
tensor_to_image(stylized_image)

पीएनजी

सामग्री और शैली के प्रतिनिधित्व को परिभाषित करें

छवि की सामग्री और शैली का प्रतिनिधित्व प्राप्त करने के लिए मॉडल की मध्यवर्ती परतों का उपयोग करें। नेटवर्क की इनपुट परत से शुरू होकर, पहले कुछ परत सक्रियण किनारों और बनावट जैसी निम्न-स्तरीय सुविधाओं का प्रतिनिधित्व करते हैं। जैसे ही आप नेटवर्क के माध्यम से कदम रखते हैं, अंतिम कुछ परतें उच्च-स्तरीय सुविधाओं का प्रतिनिधित्व करती हैं-वस्तु भाग जैसे पहिये या आंखें । इस मामले में, आप VGG19 नेटवर्क आर्किटेक्चर का उपयोग कर रहे हैं, जो एक पूर्व-प्रशिक्षित छवि वर्गीकरण नेटवर्क है। छवियों से सामग्री और शैली के प्रतिनिधित्व को परिभाषित करने के लिए ये मध्यवर्ती परतें आवश्यक हैं। एक इनपुट छवि के लिए, इन मध्यवर्ती परतों पर संबंधित शैली और सामग्री लक्ष्य अभ्यावेदन से मिलान करने का प्रयास करें।

एक VGG19 लोड करें और यह सुनिश्चित करने के लिए हमारी छवि पर परीक्षण चलाएँ कि यह सही तरीके से उपयोग किया गया है:

x = tf.keras.applications.vgg19.preprocess_input(content_image*255)
x = tf.image.resize(x, (224, 224))
vgg = tf.keras.applications.VGG19(include_top=True, weights='imagenet')
prediction_probabilities = vgg(x)
prediction_probabilities.shape
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5
574717952/574710816 [==============================] - 17s 0us/step
574726144/574710816 [==============================] - 17s 0us/step
TensorShape([1, 1000])
predicted_top_5 = tf.keras.applications.vgg19.decode_predictions(prediction_probabilities.numpy())[0]
[(class_name, prob) for (number, class_name, prob) in predicted_top_5]
[('Labrador_retriever', 0.493171),
 ('golden_retriever', 0.2366529),
 ('kuvasz', 0.036357544),
 ('Chesapeake_Bay_retriever', 0.024182785),
 ('Greater_Swiss_Mountain_dog', 0.0186461)]

अब एक VGG19 को बिना वर्गीकरण शीर्ष के लोड करें, और परत के नामों को सूचीबद्ध करें

vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')

print()
for layer in vgg.layers:
  print(layer.name)
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5
80142336/80134624 [==============================] - 2s 0us/step
80150528/80134624 [==============================] - 2s 0us/step

input_2
block1_conv1
block1_conv2
block1_pool
block2_conv1
block2_conv2
block2_pool
block3_conv1
block3_conv2
block3_conv3
block3_conv4
block3_pool
block4_conv1
block4_conv2
block4_conv3
block4_conv4
block4_pool
block5_conv1
block5_conv2
block5_conv3
block5_conv4
block5_pool

छवि की शैली और सामग्री का प्रतिनिधित्व करने के लिए नेटवर्क से मध्यवर्ती परतें चुनें:

content_layers = ['block5_conv2'] 

style_layers = ['block1_conv1',
                'block2_conv1',
                'block3_conv1', 
                'block4_conv1', 
                'block5_conv1']

num_content_layers = len(content_layers)
num_style_layers = len(style_layers)

शैली और सामग्री के लिए मध्यवर्ती परतें

तो हमारे पूर्व-प्रशिक्षित छवि वर्गीकरण नेटवर्क के भीतर ये मध्यवर्ती आउटपुट हमें शैली और सामग्री प्रतिनिधित्व को परिभाषित करने की अनुमति क्यों देते हैं?

उच्च स्तर पर, नेटवर्क के लिए छवि वर्गीकरण (जिसे करने के लिए इस नेटवर्क को प्रशिक्षित किया गया है) करने के लिए, उसे छवि को समझना चाहिए। इसके लिए कच्ची छवि को इनपुट पिक्सेल के रूप में लेने और एक आंतरिक प्रतिनिधित्व बनाने की आवश्यकता होती है जो कच्ची छवि पिक्सेल को छवि के भीतर मौजूद सुविधाओं की जटिल समझ में परिवर्तित करता है।

यह भी एक कारण है कि क्यों दृढ़ तंत्रिका नेटवर्क अच्छी तरह से सामान्यीकरण करने में सक्षम हैं: वे कक्षाओं के भीतर इनवेरिएंस और परिभाषित सुविधाओं को पकड़ने में सक्षम हैं (उदाहरण के लिए बिल्लियों बनाम कुत्ते) जो पृष्ठभूमि शोर और अन्य उपद्रवों के लिए अज्ञेयवादी हैं। इस प्रकार, कहीं न कहीं जहां कच्ची छवि को मॉडल और आउटपुट वर्गीकरण लेबल में फीड किया जाता है, मॉडल एक जटिल फीचर एक्सट्रैक्टर के रूप में कार्य करता है। मॉडल की मध्यवर्ती परतों तक पहुंच कर, आप इनपुट छवियों की सामग्री और शैली का वर्णन करने में सक्षम हैं।

मॉडल बनाएं

tf.keras.applications में नेटवर्क डिज़ाइन किए गए हैं ताकि आप Keras कार्यात्मक API का उपयोग करके आसानी से मध्यवर्ती परत मान निकाल सकें।

कार्यात्मक एपीआई का उपयोग करके एक मॉडल को परिभाषित करने के लिए, इनपुट और आउटपुट निर्दिष्ट करें:

model = Model(inputs, outputs)

यह निम्न फ़ंक्शन एक VGG19 मॉडल बनाता है जो मध्यवर्ती परत आउटपुट की सूची देता है:

def vgg_layers(layer_names):
  """ Creates a vgg model that returns a list of intermediate output values."""
  # Load our model. Load pretrained VGG, trained on imagenet data
  vgg = tf.keras.applications.VGG19(include_top=False, weights='imagenet')
  vgg.trainable = False

  outputs = [vgg.get_layer(name).output for name in layer_names]

  model = tf.keras.Model([vgg.input], outputs)
  return model

और मॉडल बनाने के लिए:

style_extractor = vgg_layers(style_layers)
style_outputs = style_extractor(style_image*255)

#Look at the statistics of each layer's output
for name, output in zip(style_layers, style_outputs):
  print(name)
  print("  shape: ", output.numpy().shape)
  print("  min: ", output.numpy().min())
  print("  max: ", output.numpy().max())
  print("  mean: ", output.numpy().mean())
  print()
block1_conv1
  shape:  (1, 336, 512, 64)
  min:  0.0
  max:  835.5256
  mean:  33.97525

block2_conv1
  shape:  (1, 168, 256, 128)
  min:  0.0
  max:  4625.8857
  mean:  199.82687

block3_conv1
  shape:  (1, 84, 128, 256)
  min:  0.0
  max:  8789.239
  mean:  230.78099

block4_conv1
  shape:  (1, 42, 64, 512)
  min:  0.0
  max:  21566.135
  mean:  791.24005

block5_conv1
  shape:  (1, 21, 32, 512)
  min:  0.0
  max:  3189.2542
  mean:  59.179478

शैली की गणना करें

एक छवि की सामग्री को मध्यवर्ती फीचर मैप्स के मूल्यों द्वारा दर्शाया जाता है।

यह पता चला है, एक छवि की शैली को विभिन्न फीचर मैप्स में साधनों और सहसंबंधों द्वारा वर्णित किया जा सकता है। एक ग्राम मैट्रिक्स की गणना करें जिसमें प्रत्येक स्थान पर फीचर वेक्टर के बाहरी उत्पाद को अपने साथ लेकर और सभी स्थानों पर उस बाहरी उत्पाद का औसत लेकर यह जानकारी शामिल है। इस ग्राम मैट्रिक्स की गणना एक विशेष परत के लिए की जा सकती है:

\[G^l_{cd} = \frac{\sum_{ij} F^l_{ijc}(x)F^l_{ijd}(x)}{IJ}\]

इसे tf.linalg.einsum फ़ंक्शन का उपयोग करके संक्षिप्त रूप से कार्यान्वित किया जा सकता है:

def gram_matrix(input_tensor):
  result = tf.linalg.einsum('bijc,bijd->bcd', input_tensor, input_tensor)
  input_shape = tf.shape(input_tensor)
  num_locations = tf.cast(input_shape[1]*input_shape[2], tf.float32)
  return result/(num_locations)

शैली और सामग्री निकालें

एक मॉडल बनाएं जो शैली और सामग्री टेंसर लौटाए।

class StyleContentModel(tf.keras.models.Model):
  def __init__(self, style_layers, content_layers):
    super(StyleContentModel, self).__init__()
    self.vgg = vgg_layers(style_layers + content_layers)
    self.style_layers = style_layers
    self.content_layers = content_layers
    self.num_style_layers = len(style_layers)
    self.vgg.trainable = False

  def call(self, inputs):
    "Expects float input in [0,1]"
    inputs = inputs*255.0
    preprocessed_input = tf.keras.applications.vgg19.preprocess_input(inputs)
    outputs = self.vgg(preprocessed_input)
    style_outputs, content_outputs = (outputs[:self.num_style_layers],
                                      outputs[self.num_style_layers:])

    style_outputs = [gram_matrix(style_output)
                     for style_output in style_outputs]

    content_dict = {content_name: value
                    for content_name, value
                    in zip(self.content_layers, content_outputs)}

    style_dict = {style_name: value
                  for style_name, value
                  in zip(self.style_layers, style_outputs)}

    return {'content': content_dict, 'style': style_dict}

जब किसी छवि पर कॉल किया जाता है, तो यह मॉडल style_layers की ग्राम मैट्रिक्स (शैली) और content_layers की सामग्री लौटाता है:

extractor = StyleContentModel(style_layers, content_layers)

results = extractor(tf.constant(content_image))

print('Styles:')
for name, output in sorted(results['style'].items()):
  print("  ", name)
  print("    shape: ", output.numpy().shape)
  print("    min: ", output.numpy().min())
  print("    max: ", output.numpy().max())
  print("    mean: ", output.numpy().mean())
  print()

print("Contents:")
for name, output in sorted(results['content'].items()):
  print("  ", name)
  print("    shape: ", output.numpy().shape)
  print("    min: ", output.numpy().min())
  print("    max: ", output.numpy().max())
  print("    mean: ", output.numpy().mean())
Styles:
   block1_conv1
    shape:  (1, 64, 64)
    min:  0.0055228462
    max:  28014.557
    mean:  263.79022

   block2_conv1
    shape:  (1, 128, 128)
    min:  0.0
    max:  61479.496
    mean:  9100.949

   block3_conv1
    shape:  (1, 256, 256)
    min:  0.0
    max:  545623.44
    mean:  7660.976

   block4_conv1
    shape:  (1, 512, 512)
    min:  0.0
    max:  4320502.0
    mean:  134288.84

   block5_conv1
    shape:  (1, 512, 512)
    min:  0.0
    max:  110005.37
    mean:  1487.0378

Contents:
   block5_conv2
    shape:  (1, 26, 32, 512)
    min:  0.0
    max:  2410.8796
    mean:  13.764149

ग्रेडिएंट डिसेंट चलाएँ

इस स्टाइल और कंटेंट एक्सट्रैक्टर के साथ, अब आप स्टाइल ट्रांसफर एल्गोरिथम को लागू कर सकते हैं। प्रत्येक लक्ष्य के सापेक्ष अपनी छवि के आउटपुट के लिए माध्य वर्ग त्रुटि की गणना करके ऐसा करें, फिर इन नुकसानों का भारित योग लें।

अपनी शैली और सामग्री लक्ष्य मान सेट करें:

style_targets = extractor(style_image)['style']
content_targets = extractor(content_image)['content']

अनुकूलित करने के लिए छवि को शामिल करने के लिए एक tf.Variable परिभाषित करें। इसे त्वरित बनाने के लिए, इसे सामग्री छवि के साथ प्रारंभ करें ( tf.Variable सामग्री छवि के समान आकार का होना चाहिए):

image = tf.Variable(content_image)

चूंकि यह एक फ्लोट छवि है, पिक्सेल मान 0 और 1 के बीच रखने के लिए एक फ़ंक्शन परिभाषित करें:

def clip_0_1(image):
  return tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=1.0)

एक अनुकूलक बनाएँ। पेपर एलबीएफजीएस की सिफारिश करता है, लेकिन Adam भी ठीक काम करता है:

opt = tf.optimizers.Adam(learning_rate=0.02, beta_1=0.99, epsilon=1e-1)

इसे अनुकूलित करने के लिए, कुल हानि प्राप्त करने के लिए दो हानियों के भारित संयोजन का उपयोग करें:

style_weight=1e-2
content_weight=1e4
def style_content_loss(outputs):
    style_outputs = outputs['style']
    content_outputs = outputs['content']
    style_loss = tf.add_n([tf.reduce_mean((style_outputs[name]-style_targets[name])**2) 
                           for name in style_outputs.keys()])
    style_loss *= style_weight / num_style_layers

    content_loss = tf.add_n([tf.reduce_mean((content_outputs[name]-content_targets[name])**2) 
                             for name in content_outputs.keys()])
    content_loss *= content_weight / num_content_layers
    loss = style_loss + content_loss
    return loss

छवि को अद्यतन करने के लिए tf.GradientTape का उपयोग करें।

@tf.function()
def train_step(image):
  with tf.GradientTape() as tape:
    outputs = extractor(image)
    loss = style_content_loss(outputs)

  grad = tape.gradient(loss, image)
  opt.apply_gradients([(grad, image)])
  image.assign(clip_0_1(image))

अब परीक्षण के लिए कुछ चरण चलाएँ:

train_step(image)
train_step(image)
train_step(image)
tensor_to_image(image)

पीएनजी

चूंकि यह काम कर रहा है, इसलिए लंबा अनुकूलन करें:

import time
start = time.time()

epochs = 10
steps_per_epoch = 100

step = 0
for n in range(epochs):
  for m in range(steps_per_epoch):
    step += 1
    train_step(image)
    print(".", end='', flush=True)
  display.clear_output(wait=True)
  display.display(tensor_to_image(image))
  print("Train step: {}".format(step))

end = time.time()
print("Total time: {:.1f}".format(end-start))

पीएनजी

Train step: 1000
Total time: 21.3

कुल भिन्नता हानि

इस बुनियादी कार्यान्वयन का एक नकारात्मक पहलू यह है कि यह बहुत अधिक उच्च आवृत्ति कलाकृतियों का उत्पादन करता है। छवि के उच्च आवृत्ति घटकों पर एक स्पष्ट नियमितीकरण शब्द का उपयोग करके इन्हें घटाएं। स्टाइल ट्रांसफर में, इसे अक्सर टोटल वेरिएशन लॉस कहा जाता है:

def high_pass_x_y(image):
  x_var = image[:, :, 1:, :] - image[:, :, :-1, :]
  y_var = image[:, 1:, :, :] - image[:, :-1, :, :]

  return x_var, y_var
35 एल10एन-प्लेसहोल्डर
x_deltas, y_deltas = high_pass_x_y(content_image)

plt.figure(figsize=(14, 10))
plt.subplot(2, 2, 1)
imshow(clip_0_1(2*y_deltas+0.5), "Horizontal Deltas: Original")

plt.subplot(2, 2, 2)
imshow(clip_0_1(2*x_deltas+0.5), "Vertical Deltas: Original")

x_deltas, y_deltas = high_pass_x_y(image)

plt.subplot(2, 2, 3)
imshow(clip_0_1(2*y_deltas+0.5), "Horizontal Deltas: Styled")

plt.subplot(2, 2, 4)
imshow(clip_0_1(2*x_deltas+0.5), "Vertical Deltas: Styled")

पीएनजी

यह दिखाता है कि कैसे उच्च आवृत्ति घटकों में वृद्धि हुई है।

इसके अलावा, यह उच्च आवृत्ति घटक मूल रूप से एक एज-डिटेक्टर है। उदाहरण के लिए, आप सोबेल एज डिटेक्टर से समान आउटपुट प्राप्त कर सकते हैं:

plt.figure(figsize=(14, 10))

sobel = tf.image.sobel_edges(content_image)
plt.subplot(1, 2, 1)
imshow(clip_0_1(sobel[..., 0]/4+0.5), "Horizontal Sobel-edges")
plt.subplot(1, 2, 2)
imshow(clip_0_1(sobel[..., 1]/4+0.5), "Vertical Sobel-edges")

पीएनजी

इससे जुड़ा नियमितीकरण नुकसान मूल्यों के वर्गों का योग है:

def total_variation_loss(image):
  x_deltas, y_deltas = high_pass_x_y(image)
  return tf.reduce_sum(tf.abs(x_deltas)) + tf.reduce_sum(tf.abs(y_deltas))
total_variation_loss(image).numpy()
149402.94

इसने दिखाया कि यह क्या करता है। लेकिन इसे स्वयं लागू करने की कोई आवश्यकता नहीं है, TensorFlow में एक मानक कार्यान्वयन शामिल है:

tf.image.total_variation(image).numpy()
array([149402.94], dtype=float32)

अनुकूलन फिर से चलाएँ

total_variation_loss के लिए वज़न चुनें:

total_variation_weight=30

अब इसे train_step फंक्शन में शामिल करें:

@tf.function()
def train_step(image):
  with tf.GradientTape() as tape:
    outputs = extractor(image)
    loss = style_content_loss(outputs)
    loss += total_variation_weight*tf.image.total_variation(image)

  grad = tape.gradient(loss, image)
  opt.apply_gradients([(grad, image)])
  image.assign(clip_0_1(image))

अनुकूलन चर को पुन: प्रारंभ करें:

image = tf.Variable(content_image)

और अनुकूलन चलाएँ:

import time
start = time.time()

epochs = 10
steps_per_epoch = 100

step = 0
for n in range(epochs):
  for m in range(steps_per_epoch):
    step += 1
    train_step(image)
    print(".", end='', flush=True)
  display.clear_output(wait=True)
  display.display(tensor_to_image(image))
  print("Train step: {}".format(step))

end = time.time()
print("Total time: {:.1f}".format(end-start))

पीएनजी

Train step: 1000
Total time: 22.4

अंत में, परिणाम सहेजें:

file_name = 'stylized-image.png'
tensor_to_image(image).save(file_name)

try:
  from google.colab import files
except ImportError:
   pass
else:
  files.download(file_name)

और अधिक जानें

यह ट्यूटोरियल मूल स्टाइल-ट्रांसफर एल्गोरिथम को प्रदर्शित करता है। स्टाइल ट्रांसफर के एक सरल अनुप्रयोग के लिए TensorFlow हब से मनमानी छवि शैली स्थानांतरण मॉडल का उपयोग करने के तरीके के बारे में अधिक जानने के लिए इस ट्यूटोरियल को देखें।