import tensorflow as tf
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras.applications.vgg19 import preprocess_input
from tensorflow.keras.models import Model
from tensorflow.keras.applications.vgg19 import VGG19
from IPython.display import clear_output
# Load images
def load_image(image_path):
img = tf.io.read_file(image_path)
img = tf.image.decode_image(img, channels=3)
img = tf.image.convert_image_dtype(img, tf.float32)
img = img[tf.newaxis, :] # Add batch dimension
img = preprocess_input(img*255) # Preprocess image
return img
# Display an image
def display_image(image, title=None):
if len(image.shape) > 3:
image = tf.squeeze(image, axis=0)
plt.imshow(image)
if title:
plt.title(title)
plt.axis("off")
plt.show()
# Content and style layers
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)
# Load images
content_image = load_image('path_to_content_image.jpg')
style_image = load_image('path_to_style_image.jpg')
# Create a vgg model
def vgg_model(layer_names):
vgg = VGG19(include_top=False, weights='imagenet')
vgg.trainable = False
outputs = [vgg.get_layer(name).output for name in layer_names]
model = Model([vgg.input], outputs)
return model
# Create a model with access to intermediate layers
style_extractor = vgg_model(style_layers)
style_outputs = style_extractor(style_image*255)
# Calculate style
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)
# Style and content models
class StyleContentModel(Model):
def __init__(self, style_layers, content_layers):
super(StyleContentModel, self).__init__()
self.vgg = vgg_model(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):
inputs = inputs*255.0
preprocessed_input = 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}
# Instantiate extractor
extractor = StyleContentModel(style_layers, content_layers)
results = extractor(tf.constant(content_image))
# Define style and content targets
style_targets = extractor(style_image)['style']
content_targets = extractor(content_image)['content']
# Initialize image to optimize
image = tf.Variable(content_image)
# Set weight of total variational loss
total_variation_weight = 1e8
style_weight = 1e-2
content_weight = 1e4
# Define a function to calculate loss
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()])
content_loss = tf.add_n([tf.reduce_mean((content_outputs[name]-content_targets[name])**2)
for name in content_outputs.keys()])
style_loss *= style_weight / num_style_layers
content_loss *= content_weight / num_content_layers
loss = style_loss + content_loss
return loss
# Create a tf.function for performance
@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(tf.clip_by_value(image, 0.0, 1.0))
# Use Adam optimizer
opt = tf.optimizers.Adam(learning_rate=0.02, beta_1=0.99, epsilon=1e-1)
# Perform optimization
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='')
clear_output(wait=True)
display_image(image.read_value())
print("Train step: {}".format(step))
Ujistěte se, že nahradíte 'path_to_content_image.jpg'
a 'path_to_style_image.jpg'
skutečnými cestami k vašim obrázkům.
Tento kód je inspirován tutoriálem TensorFlow na přenos stylu, který je k dispozici zde. Doporučuji se podívat na tento tutoriál pro podrobnější vysvětlení kódu a přenosu stylu.
Tento kód implementuje algoritmus pro přenos stylu obrázku. Kód využívá konvoluční neuronovou síť VGG19, která je předtrénovaná na datasetu ImageNet, pro extrakci vlastností z obrázků. VGG19 je populární model používaný v algoritmech pro přenos stylu kvůli jeho schopnosti zachytit jak obsah, tak stylové prvky obrázků na různých vrstvách sítě.
Kód začíná definicí několika pomocných funkcí pro načítání a zobrazování obrázků, včetně funkce load_img()
, která načítá obrázek z cesty k souboru a upravuje jeho rozměry tak, aby nejdelší strana měla 512 pixelů.
Následně jsou definovány cesty k obrázkům obsahu a stylu. Tyto obrázky jsou načteny, zpracovány a zobrazeny pomocí výše uvedených funkcí.
Poté je vytvořen model VGG19 s vahami předtrénovanými na ImageNetu. Tento model je použit k vytvoření dvou nových modelů: content_model
a style_model
. Tyto modely jsou vytvořeny tak, že vracejí výstupy ze specifických vrstev VGG19, které jsou považovány za reprezentativní pro obsah a styl obrázku.
Následuje vytvoření extraktoru, který je modelem, který na vstupu očekává obrázek a na výstupu dává výstupy z vrstev obsahu a stylu.
Poté je vytvořena proměnná image
, která je inicializována obsahovým obrázkem a bude optimalizována tak, aby se její styl co nejvíce podobal stylu stylu cílového obrázku.
Dále je definována funkce train_step()
, která provádí jeden krok gradient descentu na obrázku. Tato funkce vypočítává ztrátu stylu a obsahu mezi aktuálním obrázkem a cílovými obrázky a používá automatické diferencování a optimalizátor Adam k aktualizaci obrázku tak, aby minimalizoval tuto ztrátu.
Konečně je tento krok trénování opakován v cyklech přes zadaný počet epoch a kroků na epochu. Po každé epoše je aktuální obrázek zobrazen.
Celkově tento kód demonstroval, jak lze využít konvoluční neuronové sítě a techniky optimalizace pro účely umělecké tvorby, konkrétně pro přenos stylu mezi obrázky.
Výsledek:
Průběh tréninku v jiném příkladu (vývojové prostředí PyCharm):
Zde je stručný popis toho, co každá část kódu dělá (Implementace přenosu stylu pomocí hlubokého učení a TensorFlow):
Načítání a zobrazování obrázků: První část kódu obsahuje funkce pro načítání a zobrazování obrázků. Obrázky jsou načteny jako tensory a předzpracovány pro použití s modelem VGG19.
Definování obsahových a stylových vrstev: Model VGG19 je konvoluční neuronová síť předtrénovaná na datasetu ImageNet. V tomto kódu je použit jako extraktor obsahu a stylu. Určujeme, které vrstvy modelu VGG19 budou použity pro extrakci obsahu a stylu.
Vytvoření modelu VGG: Funkce
vgg_model
vytváří nový model, který vrací výstupy z určených vrstev.Výpočet Gramovy matice pro stylové vrstvy: Gramova matice je použita k výpočtu stylových lossů mezi stylovým obrázkem a generovaným obrázkem.
Vytvoření modelu pro extrakci obsahu a stylu: Třída
StyleContentModel
vytváří model, který vrací obsah a stylové výstupy pro daný vstup.Definování loss funkce a funkce pro trénování: Tyto funkce jsou použity k optimalizaci obrázku tak, aby minimalizovaly loss mezi obsahem/stylovými výstupy generovaného obrázku a cílovými obsahovými/stylovými výstupy.
Optimalizace obrázku: Nakonec je obrázek optimalizován pomocí Adam optimizeru. Trénovací smyčka prochází určitým počtem epoch a kroků na epochu, přičemž v každém kroku je aktualizován generovaný obrázek.