Python深度学习基于Tensorflow(12)实战生成式模型

文章目录

        • Deep Dream
        • 风格迁移
        • 参考资料

Deep Dream

DeepDream 是一项将神经网络学习模式予以可视化展现的实验。与孩子们观察云朵并尝试解释随机形状相类似,DeepDream 会过度解释并增强其在图像中看到的图案。

DeepDream为了说明CNN学习到的各特征的意义,将采用放大处理的方式。具体来说就是使用梯度上升的方法可视化网络每一层的特征,即用一张噪声图像输入网络,反向更新的时候不更新网络权重,而是更新初始图像的像素值,以这种“训练图像”的方式可视化网络。DeepDream正是以此为基础。

DeepDream如何放大图像特征?这里我们先看一个简单实例。比如:有一个网络学习了分类猫和狗的任务,给这个网络一张云的图像,这朵云可能比较像狗,那么机器提取的特征可能也会像狗。假设对应一个特征最后输入概率为[0.6, 0.4], 0.6表示为狗的概率, 0.4表示为猫的概率,那么采用L2范数可以很好达到放大特征的效果。对于这样一个特征,L2 =〖x1〗2+〖x2〗2,若x1越大,x2越小,则L2越大,所以只需要最大化L2就能保证当x1>x2的时候,迭代的轮数越多x1越大,x2越小,所以图像就会越来越像狗。每次迭代相当于计算L2范数,然后用梯度上升的方法调整图像。优化的就不再是优化权重参数,而是特征值或像素点,因此,构建损失函数时,不使用通常的交叉熵,而是最大化特征值的L2范数。使图片经过网络之后提取的特征更像网络隐含的特征。

使用基本图像,它输入到预训练的CNN。 然后,正向传播到特定层。为了更好理解该层学到了什么,我们需要最大化通过该层激活值。以该层输出为梯度,然后在输入图像上完成渐变上升,以最大化该层的激活值。不过,光这样做并不能产生好的图像。为了提高训练质量,需要使用一些技术使得到的图像更好。可以进行高斯模糊以使图像更平滑,使用多尺度(又称为八度)的图片进行计算。先连续缩小输入图像,然后,再逐步放大,并将结果合并为一个图像输出。

首先使用预训练模型 InceptionV3 对图像特征进行提取,其中 mixed 表示的是 InceptionV3 中的 mixed 层的特征值;

import numpy as np
import tensorflow as tf
import matplotlib.pyplot as pltlayer_coeff = {"mixed4": 1.0,"mixed5": 1.5,"mixed6": 2.0,"mixed7": 2.5,
}model = tf.keras.applications.inception_v3.InceptionV3(weights="imagenet", include_top=False)
outputs_dict = dict([(layer.name, layer.output) for layer in [model.get_layer(name) for name in layer_coeff.keys()]])
feature_extractor = tf.keras.Model(inputs=model.inputs, outputs=outputs_dict)

计算损失:

def compute_loss(input_image):features = feature_extractor(input_image)loss_list = []for name in features.keys():coeff = layer_settings[name]activation = features[name]# 通过仅在损失中包含非边界像素来避免边界伪影scaling = tf.reduce_prod(tf.cast(tf.shape(activation), "float32"))loss_list.append(coeff * tf.reduce_sum(tf.square(activation[:, 2:-2, 2:-2, :])) / scaling)return tf.reduce_sum(loss_list)

定义训练函数:

@tf.function
def train_step(img, learning_rate=1e-1):with tf.GradientTape() as tape:tape.watch(img)loss = compute_loss(img)grads = tape.gradient(loss, img)grads /= tf.math.reduce_std(grads)img += learning_rate * gradsimg = tf.clip_by_value(img, -1, 1)return loss, imgdef train_loop(img, iterations, learning_rate=1e-1, max_loss=None):for i in range(iterations):loss, img = gradient_ascent_step(img, learning_rate)if max_loss is not None and loss > max_loss:breakreturn img

定义超参数:

# 缩放次数 多尺度次数 也即八度 每一次缩放 octave_scale
num_octave = 1
# 缩放倍数
octave_scale = 1.4
# train_loop 训练迭代次数
iterations = 80
# 最大损失
max_loss = 15
# 学习率
learning_rate = 1e-2

如下便是多尺度缩放的训练过程:

![[Pasted image 20240520015509.png]]

定义数据:

img = preprocess_image('./dog.jpg')
plt.imshow(deprocess(img[0]))

![[Pasted image 20240520015056.png]]

开始训练:

original_img = preprocess_image('./dog.jpg')
original_shape = original_img.shape[1:3]successive_shapes = [original_shape]
for i in range(1, num_octave):shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape])successive_shapes.append(shape)
successive_shapes = successive_shapes[::-1]shrunk_original_img = tf.image.resize(original_img, successive_shapes[0])img = tf.identity(original_img)  # Make a copy
for i, shape in enumerate(successive_shapes):print("Processing octave %d with shape %s" % (i, shape))img = tf.image.resize(img, shape)img = train_loop(img, iterations=iterations, learning_rate=learning_rate, max_loss=max_loss)upscaled_shrunk_original_img = tf.image.resize(shrunk_original_img, shape)same_size_original = tf.image.resize(original_img, shape)lost_detail = same_size_original - upscaled_shrunk_original_imgimg += lost_detailshrunk_original_img = tf.image.resize(original_img, shape)tf.keras.preprocessing.image.save_img('./dream-' + "dog.jpg", deprocess(img[0]))

![[Pasted image 20240520014920.png]]

总的来说,Deep Dream 相当于训练可视化,其不对参数进行梯度更新,而是对图像进行梯度更新,通过梯度上升让图像能够最大程度的激活目标层的输出结果;其模型实际意义不强,有稍微的模型解释性;

风格迁移

风格迁移的本质和 Deep Dream 是一样的,其主要还是因为风格转换涉及到的样本数量太少,基本就是两张图片之间进行转化,因此对参数进行梯度更新是不现实的,我们只能利用预训练模型,提取图片特征然后定义特征之间的损失进而进行操作;实现风格迁移的核心思想就是定义损失函数。

风格迁移的损失函数由内容损失和风格损失组成,这里用 O i m a g e O_{image} Oimage 表示原图, R i m a g e R_{image} Rimage 表示风格图, G i m a g e G_{image} Gimage 表示生成图,那么损失如下: L = d i s t a n c e ( s t y l e ( R i m a g e ) − s t y l e ( G i m a g e ) ) + d i s t a n c e ( c o n t e n t ( O i m a g e ) − c o n t e n t ( G i m a g e ) ) \mathcal{L} = distance(style(R_{image}) - style(G_{image})) + distance(content(O_{image}) - content(G_{image})) L=distance(style(Rimage)style(Gimage))+distance(content(Oimage)content(Gimage))
卷积神经网络不同层学到的图像特征是不一样的,靠近输入端的卷积层学到的是图像比较具体,局部的特征,如位置,形状,颜色,纹理等。靠近输出端的卷积层学到的是图像更全面,更抽象的特征,但会丢失图像的一些详细信息;

风格损失

风格损失是利用 Gram矩阵 来计算的,Gram矩阵 将图像的通道作为一个维度,将图像的宽和高合并作为一个维度,得到 X X X 的尺寸为 [ c h a n n e l , w ∗ h ] [channel, w*h] [channel,wh],然后计算 X ⋅ X T X \cdot X^T XXT ,用该值来衡量风格;

@tf.function
def gram_matrix(image):image = tf.transpose(image, (2, 0, 1))image = tf.reshape(image, [tf.shape(image)[0], -1])gram = tf.matmul(image, image, transpose_b=True)return gram@tf.function
def compute_style_loss(r_image, g_image):r_w, r_h, r_c = tf.shape(r_image)g_w, g_h, g_c = tf.shape(g_image)r_gram = gram_matrix(r_image)g_gram = gram_matrix(g_image)style_loss = tf.reduce_sum(tf.square(r_gram - g_gram))/  (4 * (r_c * g_c) * (r_w * r_h * g_w * g_h))

内容损失

内容损失很简单,也就是生成图像和原来图像之间的区别;

@tf.function
def compute_content_loss(o_image, g_image):return tf.reduce_sum(tf.square(o_image - g_image))

这里不需要放缩是因为没有像风格损失一样经历过 Gram矩阵 计算,这就导致原本的内容并没有经过扩大,不过后面同样会给内容损失和风格损失分配权重;

总损失

总损失让生成的图像具有连续性,不要这里一块那里一块;

def compute_variation_loss(x):a = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, 1:, :tf.shape(x)[2]-1, :])b = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, :tf.shape(x)[1]-1, 1:, :])return tf.reduce_sum(tf.pow(a+b, 1.25))

这里还是以上面的小狗图片作为原图片,风格图片采取梵高的星空图片;

![[Pasted image 20240520202401.png]]

首先导入预训练模型 VGG19,以及图像处理函数 preprocess_image deprocess_image

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as pltdef preprocess_image(image_path):img = tf.keras.preprocessing.image.load_img(image_path, target_size=(400, 600))img = tf.keras.preprocessing.image.img_to_array(img)img = np.expand_dims(img, axis=0)img = tf.keras.applications.vgg19.preprocess_input(img)return tf.convert_to_tensor(img)def deprocess_image(x):x = x.reshape((400, 600, 3))x[:, :, 0] += 103.939x[:, :, 1] += 116.779x[:, :, 2] += 123.68x = x[:, :, ::-1]x = np.clip(x, 0, 255).astype("uint8")return x# 用于风格损失的网络层列表
style_layer_names = ["block1_conv1","block2_conv1","block3_conv1","block4_conv1","block5_conv1",
]
# 用于内容损失的网络层
content_layer_names = ["block5_conv2",
]model = tf.keras.applications.vgg19.VGG19(weights="imagenet", include_top=False)
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers if layer.name in style_layer_names + content_layer_names])
feature_extractor = tf.keras.Model(inputs=model.inputs, outputs=outputs_dict)

定义三个损失:compute_style_loss compute_content_loss compute_variation_loss

def gram_matrix(image):image = tf.transpose(image, (2, 0, 1))image = tf.reshape(image, [tf.shape(image)[0], -1])gram = tf.matmul(image, image, transpose_b=True)return gramdef compute_style_loss(r_image, g_image):r_w, r_h, r_c = tf.cast(tf.shape(r_image)[0], tf.float32), tf.cast(tf.shape(r_image)[1], tf.float32), tf.cast(tf.shape(r_image)[2], tf.float32)g_w, g_h, g_c = tf.cast(tf.shape(g_image)[0], tf.float32), tf.cast(tf.shape(g_image)[1], tf.float32), tf.cast(tf.shape(g_image)[2], tf.float32)r_gram = gram_matrix(r_image)g_gram = gram_matrix(g_image)style_loss = tf.reduce_sum(tf.square(r_gram - g_gram))/  (4 * (r_c * g_c) * (r_w * r_h * g_w * g_h))return style_lossdef compute_content_loss(o_image, g_image):return tf.reduce_sum(tf.square(o_image - g_image))def compute_variation_loss(x):a = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, 1:, :tf.shape(x)[2]-1, :])b = tf.square(x[:, :tf.shape(x)[1]-1, :tf.shape(x)[2]-1, :] - x[:, :tf.shape(x)[1]-1, 1:, :])return tf.reduce_sum(tf.pow(a+b, 1.25))

定义损失比例以及总损失计算函数 compute_loss

total_weight = 1e-6
style_weight = 1e-6
content_weight = 2.5e-8def compute_loss(o_image, r_image, g_image):X = tf.concat([o_image, r_image, g_image], axis=0)features = feature_extractor(X)loss_list = []for content_layer_name in content_layer_names:temp = features[content_layer_name]o_image_ = temp[0,:,:,:]g_image_ = temp[2,:,:,:]loss = compute_content_loss(o_image_, g_image_)loss_list.append(loss*content_weight/len(content_layer_names))for style_layer_name in style_layer_names:temp = features[style_layer_name]r_image_ = temp[1,:,:,:]g_image_ = temp[2,:,:,:]loss = compute_style_loss(r_image_, g_image_)loss_list.append(loss*style_weight/len(style_layer_names))loss = compute_variation_loss(g_image)loss_list.append(loss*total_weight)return tf.reduce_sum(loss_list)

定义优化器,图片以及开始训练:

o_image = preprocess_image('./dog.jpg')
r_image = preprocess_image('./start-night.png')
g_image = tf.Variable(o_image)optimizer = tf.keras.optimizers.Adam(learning_rate=1)def train_step():with tf.GradientTape() as tape:loss = compute_loss(o_image, r_image, g_image)grads = tape.gradient(loss, g_image)optimizer.apply_gradients([(grads, g_image)])return lossfor epoch in range(100):plt.imshow(deprocess_image(g_image.numpy()))plt.axis('off')plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))plt.show()tf.print(train_step())

最后将 生成的图片 转化为 GIF

import imageio
from PIL import Image
import os
import numpy as np# 这里与 for epoch in range(100): 中的图片名称对应 image_at_epoch_{:04d}.png
converted_images = [np.array(Image.open(item)) for item in [file for file in os.listdir('./') if file.startswith('image')]]
imageio.mimsave("animation.gif", converted_images, fps=15)

得到如下结果:
![[animation 2.gif]]

参考资料

DeepDream | TensorFlow Core (google.cn)

【数学-20】格拉姆矩阵(Gram matrix)详细解读 - 知乎 (zhihu.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「51媒体」线下活动媒体同步直播,云分发,分流直播

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。 线下活动除了邀请嘉宾,邀请媒体,邀请行业大咖KOL,来为活动站台,背书外,我们也可以将线下的活动同步在线上进行直播&#xff0c…

Codeforces Round 821 (Div. 2) C. Parity Shuffle Sorting (构造之全变成一样的)

给你一个数组 a a a &#xff0c;其中有 n n n 个非负整数。你可以对它进行以下操作。 选择两个索引 l l l 和 r r r ( 1 ≤ l < r ≤ n ) ( 1≤l<r≤n ) (1≤l<r≤n)。 如果 a l a r a_la_r al​ar​ 是奇数&#xff0c;则进行 a r : a l a_r:a_l ar​:al​…

react【框架原理详解】JSX 的本质、SyntheticEvent 合成事件机制、组件渲染过程、组件更新过程

JSX 的本质 JSX 代码本身并不是 HTML&#xff0c;也不是 Javascript&#xff0c;在渲染页面前&#xff0c;需先通过解析工具&#xff08;如babel&#xff09;解析之后才能在浏览器中运行。 babel官网可查看 JSX 解析后的效果 更早之前&#xff0c;Babel 会把 JSX 转译成一个 R…

AI大模型探索之路-实战篇4:DB-GPT数据应用开发框架调研实践

目录 前言一、DB-GPT总体概述二、DB-GPT关键特性1、私域问答&数据处理&RAG2、多数据源&GBI3、多模型管理4、自动化微调5、Data-Driven Multi-Agents&Plugins6、隐私安全 三、服务器资源准备1、创建实例2、打开jupyterLab 四、DB-GPT启动1、激活 conda 环境2、切…

区块链fisco联盟链搭建(二)搭建多群组联盟链

本文章只讲搭建的命令方法 以单机、四机构、三群组、八节点的星形组网拓扑为例 第一步创建并进入工作目录&#xff08;继续以fisco为例&#xff09; mkdir /fisco cd /fisco 获取搭链脚本上一篇文章区块链fisco联盟链搭建 (一)搭建单群组四节点联盟链中有 第二步生成多群组…

抖音小店没有流量不出单?归根到底,就是转化率不行!

哈喽~我是电商月月 新手做抖音小店&#xff0c;最忧愁的就是&#xff1a;店铺不出单怎么办&#xff1f; 商家通常会把没有销量的原因&#xff0c;都推向于“店铺没有流量” 但在抖音&#xff0c;这个日活量高达9亿的平台来说&#xff0c;任何商铺最不缺的应该就是流量了 但…

61850的总体建模原则

IEC 61850标准是电力系统自动化领域的一个重要标准,它定义了数据的模型和设备描述,使得不同厂家的设备之间能够实现互操作性。下面将围绕“61850的总体建模原则”展开讨论,主要包括物理设备建模基础、逻辑设备组合规则、逻辑节点功能划分、数据模型统一标准、配置文件规范描…

炒股前你要知道的股票知识

一、股票组成 A股股票组成板块有:地区板块、行业板块、证监会板块,概念板块。 其中各个板块还可以分为: A农、林、牧、渔业; B采矿业; C制造业; D电力、热力、燃气及水生产和供应业; E建筑业; F批发和零售业; G交通运输、仓储和邮政业; H住宿和餐饮业; I…

《Qt》使用Windeployqt发布程序

之前都是使用QTVS开发&#xff0c;这次直接使用QT开发&#xff0c;记录一下程序发布过程&#xff0c;方便后期使用查阅。 添加环境变量 在path目录下添加如下路径&#xff1a; 之前使用QTVS2013&#xff0c;添加如下路径 D:\App\Qt5.9.3\5.9.3\msvc2013_64\bin; D:\App\Qt…

dll文件是什么?电脑丢失某个dll文件有什么解决办法

Dll文件是什么&#xff1f;这个文件在电脑中是什么样的地位&#xff1f;如果电脑提示丢失了某个dll文件那么有什么办的解决这个问题呢&#xff1f;如何将丢失的dll文件进行修复呢&#xff1f;今天这篇文章将按就来教大家几种修复丢失dll文件问题的方法。 DLL 文件&#xff0c;全…

[Redis]基本全局命令

Redis存储方式介绍 在 Redis 中数据是以键值对的凡事存储的&#xff0c;键&#xff08;Key&#xff09;和值&#xff08;Value&#xff09;是基本的数据存储单元。以下是对 Redis 键值对的详细讲解&#xff1a; 键&#xff08;Key&#xff09;&#xff1a; 类型&#xff1a;…

JVM、JRE和JDK的区别

首先需要确定的是JDK里是包含JRE的&#xff0c;而JRE里又包含JVM&#xff0c;它们区别在于面向的服务对象不同所以进行了不同的包装。 JVM&#xff1a;JVM是面向操作系统&#xff0c;.Class字节码->机器码以及程序运行的内存的管理。 JRE&#xff1a;JRE是面向于程序的&am…

全局配置路径无法识别的解决——后端

在全局配置路径reggie.path的时候&#xff0c;无法正常启动SpringBoot项目 Value("${reggie.path}")private String basePath; 查看application.yml的配置情况: 发现path没有起作用&#xff0c;推测是格式问题&#xff0c;冒号后面空格后即可

Web API——获取DOM元素

目录 1、根据选择器来获取DOM元素 2.、根据选择器来获取DOM元素伪数组 3、根据id获取一个元素 4、通过标签类型名获取所有该标签的元素 5、通过类名获取元素 目标&#xff1a;能查找/获取DOM对象 1、根据选择器来获取DOM元素 语法&#xff1a; document.querySelector(css选择…

关于性能问题优化的小讨论

大家好&#xff0c;我是阿赵。   最近很流行把之前制作在安卓或者iOS端的游戏转成微信小程序上架&#xff0c;我所在的项目也有这样的操作。微信小程序是用WebGL来运行的&#xff0c;实际上它的性能很差&#xff0c;只有不到app端的三分之一的性能可用&#xff0c;内存方面也…

LabVIEW机器视觉技术对工业制造有什么影响?

LabVIEW机器视觉技术对工业制造产生了深远的影响&#xff0c;主要体现在以下几个方面&#xff1a; 1. 提高生产效率 LabVIEW机器视觉技术可以自动检测和分析生产线上的产品&#xff0c;提高检测速度和精度。传统的人工检测方式往往效率低下且容易出错&#xff0c;而机器视觉系…

java 数组的常见操作

在 Java 中&#xff0c;数组是一种特殊的对象&#xff0c;用于存储相同类型的多个元素。以下是一些常见的数组操作&#xff1a; 声明数组&#xff1a;使用以下语法声明一个数组&#xff0c;其中 type 是数组元素的数据类型&#xff0c;name 是数组的名称。 type[] name;例如&…

第19讲:自定义类型:结构体

目录 1.结构体类型的声明1.1 结构体回顾1.1.1 结构的声明 特殊的结构声明1.3 结构的⾃引⽤ 2. 结构体内存的对齐2.2 为什么存在内存对⻬?2.3 修改默认对⻬数 3. 结构体传参4. 结构体实现位段4.1 什么是位段4.2 位段的内存分配4.3 位段的跨平台问题4.5 位段使⽤的注意事项 正文…

梳理 JavaScript 中空数组调用 every方法返回true 带来惊讶的问题

前言 人生总是在意外之中. 情况大概是这样的. 前两天版本上线以后, 无意中发现了一个bug, 虽然不是很大, 为了不让用户使用时感觉到问题. 还是对着一个小小的bug进行了修复, 并重新在上线一次, 虽然问题不大, 但带来的时间成本还是存在的. 以及上线后用户体验并不是很好. 问题…

JVM学习-垃圾收集器(二)

Serial回收器&#xff1a;串行回收 Serial收集器是最基本、历史最悠久的收集器JDK1.3之前新生代唯一的选择Hotpot中Client模式下的默认新生代垃圾收集器采用复制算法&#xff0c;串行回收“Stop-the-world”机制的方式执行内存回收除了年轻代之外&#xff0c;Serial收集器还提…