自己写个 Prisma
前一段时间特别火的 Prisma 大家都玩了么,看了这篇文章后,你也可以自己写一个 Prisma 迷你版了。
这个 idea 最开始起源于 Google Research Blog Here's the initial Google DeepDream blog post:
他们用大量的图片数据来训练深度神经网络,使这个网络可以判断出图片中的事物,然后投入一个新的图片,让图片识别,不仅仅是识别,还要把图片修正为网络学到的东西。
然后另一个团队发表了一篇相似的论文
他们用名画来训练模型,然后投入一个生活中的图片,通过强化一些 feature,将这个图片修正为更像名画风格的图片。
原理就是用一个 Convolutional Neural Network 学习一张图片的 style ,然后把另一张图片转换成这种 style。
用到的工具是 python 和 keras 包,文章后面有作者的源码的地址。
引入需要的包
from scipy.misc import imread, imresize, imsave
from scipy.optimize import fmin_l_bfgs_b
from sklearn.preprocessing import normalize
import numpy as np
import time
import os
import argparse
import h5py
from keras.models import Sequential
from keras.layers.convolutional import Convolution2D, ZeroPadding2D, AveragePooling2D
from keras import backend as K
定义三个图片变量
#Define base image, style image, and result image paths
args = parser.parse_args()
base_image_path = args.base_image_path
style_reference_image_path = args.style_reference_image_path
result_prefix = args.result_prefix
引用事先计算好的 weights vgg16
这是提前训练好的,可以识别生活中的图片,以它作为模型的起点。
#Get the weights file
weights_path = r"vgg16_weights.h5"
定义 booleans 决定是否 reshape 图片
#Init bools to decide whether or not to resize
rescale_image = strToBool(args.rescale_image)
maintain_aspect_ratio = strToBool(args.maintain_aspect_ratio)
然后初始化 style-content weights
什么是style-content weights?
在神经网络学习的过程中,不同的层学到的东西是不一样的,例如识别一个小狗,一层学到的是 edge,下一层学到的是 shape,再下一层是更复杂的 shape,最后学到的是整个的 dog。
在学习艺术风格的网络中发现,低层次学到的是 style,如纹理 颜色框架等,高层次学到的是 content,如太阳等具体的物体,CNN会把 content 和 style 分离开,所以要达到不同的效果,需要不同的权重分配。
# Init variables for style and content weights.
total_variation_weight = args.tv_weight
style_weight = args.style_weight * args.style_scale
content_weight = args.content_weight
然后设定图片维度,定义tensor代表三个图片 base image,style image,output image。
# Init dimensions of the generated picture.
img_width = img_height = args.img_size
assert img_height == img_width, 'Due to the use of the Gram matrix, width and height must match.'
img_WIDTH = img_HEIGHT = 0
aspect_ratio = 0
# get tensor representations of our images
base_image = K.variable(preprocess_image(base_image_path, True))
style_reference_image = K.variable(preprocess_image(style_reference_image_path))
# this will contain our generated image
combination_image = K.placeholder((1, 3, img_width, img_height))
再组合到一个 tensor 中
# combine the 3 images into a single Keras tensor
input_tensor = K.concatenate([base_image,
style_reference_image,
combination_image], axis=0)
放在一个 tensor 中,因为更容易被 神经网络 解析,这样一个高维的图片也可以有可以计算的复杂度。
建立 31 层的神经网络
# build the VGG16 network with our 3 images as input
first_layer = ZeroPadding2D((1, 1))
first_layer.set_input(input_tensor, shape=(3, 3, img_width, img_height))
model = Sequential()
model.add(first_layer)
model.add(Convolution2D(64, 3, 3, activation='relu', name='conv1_1'))
model.add(ZeroPadding2D((1, 1)))
model.add(Convolution2D(64, 3, 3, activation='relu'))
model.add(AveragePooling2D((2, 2), strides=(2, 2)))
。。。
一共有3种: convolution2D layer:拥有可学习的filters,这些filters有receptive field,用来将神经元连接到下一层的一个局部的区域,而不是连接到每一个神经元
ZeroPadding layer:用来控制 output 的大小
Pooling layer:只用图片的子集来计算,减少参数数量,用来避免 overfitting。
激活函数用的是 ReLU,比sigmoid更快一些。
各个层的参数分别是:
定义完模型后,引入 vgg16 的权重
# load the weights of the VGG16 networks
load_weights(weights_path, model)
定义 Loss Function:计算预测和实际的差别
# get the symbolic outputs of each "key" layer (we gave them unique names).
outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])
# get the loss (we combine style, content, and total variation loss into a single scalar)
loss = get_total_loss(outputs_dict)
得到 gradients
# get the gradients of the generated image wrt the loss
grads = K.gradients(loss, combination_image)
最后用 back propagation 训练模型,此处用到的算法是 limit-memory BFGS,可以最小化 loss function 而且空间效率较高。
#combine loss and gradient
f_outputs = combine_loss_and_gradient(loss, grads)
# Run scipy-based optimization (L-BFGS) over the pixels of the generated image to minimize the neural style loss
# 5 Step process
x, num_iter = prepare_image()
for i in range(num_iter):
#Step 1 - Record iterations
print('Start of iteration', (i+1))
start_time = time.time()
#Step 2 - Perform l_bfgs optimization function using loss and gradient
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
print('Current loss value:', min_val)
#Step 3 - Get the generated image
img = deprocess_image(x.reshape((3, img_width, img_height)))
#Step 4 - Maintain aspect ratio
if (maintain_aspect_ratio) & (not rescale_image):
img_ht = int(img_width * aspect_ratio)
print("Rescaling Image to (%d, %d)" % (img_width, img_ht))
img = imresize(img, (img_width, img_ht), interp=args.rescale_method)
if rescale_image:
print("Rescaling Image to (%d, %d)" % (img_WIDTH, img_HEIGHT))
img = imresize(img, (img_WIDTH, img_HEIGHT), interp=args.rescale_method)
最后,rescale 并且保存图片
#Step 5 - Save the generated image
fname = result_prefix + '_at_iteration_%d.png' % (i+1)
imsave(fname, img)
end_time = time.time()
print('Image saved as', fname)
print('Iteration %d completed in %ds' % (i+1, end_time - start_time))
这个算法也可以用到视频中。
另外还找到一篇《我是如何用TensorFlow 做出属于自己的Prisma的?》
感兴趣就动手写一下吧。
The code for this video is here: Here's the initial Google DeepDream blog post: A Deepdream web app: The Neural Style Paper:
- python验证码识别实战
- WCF的三个名称/命名空间,你是否傻傻分不清楚?
- .NET Core的日志[1]:采用统一的模式记录日志
- ASP.NET MVC三个重要的描述对象:ParameterDescriptor
- Ngxtop-Nginx日志实时分析利器
- 数据在内存中的存储
- 数据抓取与利用行为的不正当竞争法规制
- python学习手册-爬爬那些年咱没见过的校花图片
- 正则表达式游戏的答案
- .NET Core的日志[2]:将日志输出到控制台
- 2017年高等教育十大战略性技术(二)
- 通过实例模拟ASP.NET MVC的Model绑定机制:数组
- 《全球贸易信息动态》
- .NET Core的日志[3]:将日志写入Debug窗口
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 经验——SpringBoot 获取 resource 目录下的文件
- 聚类热图怎么按自己的意愿调整分支的顺序?
- H5|HTTP-FLV|WS-FLV|HLS|RTMP免费直播点播播放器如何自定义层叠DIV全屏后显示在视频上方?
- 什么是时间分片(Time Slicing)?
- 48岁的C语言,你知道它背后的历史吗?
- 【技术创作101训练营】我是如何使用freemarker生成Word文件的?
- 4种主流超参数调优技术
- PyTorch 最佳实践:模型保存和加载
- 突击并发编程JUC系列-启航篇
- 拜托!别再问我多线程的这些问题了
- Tomcat 架构原理解析到架构设计借鉴
- C++核心准则T.65:使用标签分发提供函数的不同实现
- Java开发中Websocket的技术选型参考
- Java 15正式发布,腾讯做出了突出贡献
- Mybatis是如何向Spring注册Mapper的