作者介绍:hardmaru ,一位钟情于 RNN 的深度学习研究者,相关地址
Twitter 地址:twitter.com/hardmaru
Github 地址:github.com/hardmaru
博客地址:blog.otoro.net/

这篇文章适用于没有任何机器学习背景的读者 , 目标是 向 艺术家和设计师 展示 如何使用一个预训练的神经网络 并 使用简单的Javascript 和 p5.js 库 生成 交互式的 数字作品。

介绍
近年来,机器学习已经成为创意社区的流行工具。比如风格转变 、 T-SNE、自动编码器、生成对抗性的网络 以及无数其他的方法,艺术家们采用这些技术生成文本、音乐以及声音。
这篇文章使用了与distill.pub 项目相同的手写模型
手写大脑建模
当写信时,大脑中有许多事情发生。比如写什么、选择词汇、 动笔写 等过程 。而 创建一个JavaScript 模型来模拟整个人类大脑写信是困难的,因此只专注于手写过程中的最后一部分 —— 笔的位置,以及笔与纸张是否接触。
对模型做两个假设。第一个假设是模型接下来要写的内容只取决于它过去写的内容。内容的记忆可以通过构建回归神经网络(RNN )完成。
通过RNN ,可以将模糊知识直接存储到 RNN 的神经元,并将此对象作为 RNN 的隐藏状态。这个隐藏状态记录 每个神经元的活跃程度,隐藏状态对象会随着写入内容而不断更新。
第二个假设是该模型不完全确定它接下来应该写什么。这两个假设可以总结为下图,描述了使用具有隐藏状态的循环神经网络模型来生成随机序列的过程。

用于手写体的RNN
用已预训练好的RNN 模型完成上一节描述的书写任务。本节将描述如何在 JavaScript 中使用该模型,下面是整个p5.j s 的框架
首先需要定义几个变量表示笔的轨迹(x,y ),此外还需要较小的坐标偏移(dx,dy) ,并用(x,y )累积( dx,dy )判断笔的走向,。
var x, y; // absolute coordinates of where the pen is
var dx, dy; // offsets of the pen strokes, in pixels
另外,笔不会总与纸张接触。定义变量pen ,若 pen 为零,则当前时刻笔与纸张相互 接触。另外需要跟踪以前时刻的pen ,记为 prev_pen 。
// keep track of whether pen is touching paper. 0 or 1.
var pen;
var prev_pen; // pen at the previous timestep
若 有了 模型每一 时刻 生成的(dx, dy, pen)变量列表,那么 使用这些数据 可以 绘制出模型在屏幕上生成的内容。开始时将 这些变量(dx, dy, x, y, pen,prev_pen )初始化为零。定义一些将由 RNN 模型使用的变量对象:
var rnn_state; // store the hidden states the rnn
// store all the parameters of a mixture-density distribution
var pdf;
// controls the amount of uncertainty of the model
// the higher the temperature, the more uncertainty.
var temperature = 0.65; // a non-negative number.
rnn_state 变量代表 RNN 的隐藏状态, 使用代码update 更新 rnn_state 。
rnn_state = Model.update([dx, dy, prev_pen], rnn_state);
rnn_state 将用于 产生 模型将要写 的内容的 概率分布。该概率分布 将 表示作为主体,称作pdf 。使用 get_pdf 就能 从rnn_state 获得 pdf :
pdf = Model.get_pdf(rnn_state);
额外变量temperature 使我们能够控制该模型是否为想要的 模型。结合pdf, 可以使用 sample 函数去采样下 一组(dx, dy, pen), 在稍后使用以下函数:
[dx, dy, pen] = Model.sample(pdf, temperature);
现在唯一需要的其他变量是控制手写体的颜色,并且跟踪浏览器的屏幕尺寸:
// stores the browser's dimensions
var screen_width = window.innerWidth;
var screen_height = window.innerHeight;
// colour for the handwriting
var line_color;
现在准备初始化所有这些声明的变量, 创建函数restart 初始化这些变量, 通过重复调用以 初始化很多次。
function restart() {
// set x to be 50 pixels from the left of the canvas
x = 50;
// set y somewhere in middle of the canvas
y = screen_height/2;
// initialize pen's states to zero.
dx = 0;
dy = 0;
prev_pen = 0;
// note: we draw lines based off previous pen's state
// randomise the rnn's initial hidden states
rnn_state = Model.random_state();
// randomise colour of line by choosing RGB values
line_color = color(random(255), random(255), random(255))
}
创建restar函数 后,定义p 5.js setup 函数初始化 框架 。
function setup() {
restart(); // initialize variables for this demo
createCanvas(screen_width, screen_height);
frameRate(60); // 60 frames per second
// clear the background to be blank white colour
background(255);
fill(255);
}
p5.j s 框架中的 draw 函数能够产生一代手写字迹, 该函数每秒调用60 次, 每次调用此函数时,RNN 将在屏幕上绘制一些东西。
function draw() {
// using the previous pen states, and hidden state
// to get next hidden state
rnn_state = Model.update([dx, dy, prev_pen], rnn_state);
// get the parameters of the probability distribution
// from the hidden state
pdf = Model.get_pdf(rnn_state);
// sample the next pen's states
// using our probability distribution and temperature
[dx, dy, pen] = Model.sample(pdf, temperature);
// only draw on the paper if pen is touching the paper
if (prev_pen == 0) {
// set colour of the line
stroke(line_color);
// set width of the line to 2 pixels
strokeWeight(2.0);
// draw line connecting prev point to current point.
line(x, y, x+dx, y+dy);
}
// update the absolute coordinates from the offsets
x += dx;
y += dy;
// update the previous pen's state
// to the current one we just sampled
prev_pen = pen;
// if the rnn starts drawing close to the right side
// of the screen, restart our demo
if (x > screen_width - 50) {
restart();
// reset screen
background(255);
fill(255);
}
}
在每一帧,draw函数 将更新基于它先前在屏幕上绘制的模型隐藏的状态。从这个隐藏状态,模型将生成接下来的概率分布。 基于此 概率分布,沿着temperature 参数, 将随机抽样 下 一组(dx, dy, pen) 变量的 新形式。基于这一新的变量集合, 产生新的手写体 。

变化温度的概率分布采样
变量 pdf 在每时段 应该储存下一个笔划的概率分布 ,它 实际上只是包含了复杂的概率分布的参数。
处理这个问题的直接方式是将概率分布建模为许多正态分布加在一起的总和, 更多的技术细节可以在 之前得到 。
利用 概率分布以及从分布中采样得到的 一组 (dx, dy, pen) 值, 以 确定接下来 绘制什么,使用 temperature 参数来控制模型的不确定性的水平。
在下面的草图中,可以通过改变温度参数来可视化概率分布是 如何增加。
为了简单起见,上述演示模拟了二十个一维正态分布与温度参数的混合。在手写模型中,概率分布是二十个二维正态分布的混合。在下一个草图中,您可以在手写模型正在写入内容时修改其温度,以查看手写随温度变化的情况。
当温度保持低时,手写模型变得非常确定,所以手写通常更整洁和更现实。
扩展手写体demo
将机器学习与设计相结合的一个更有趣的方面是探索人与机器之间的相互作用。典型的机器学习框架+ python 栈使得难以部署真正的交互式 web 应用程序。
一个可能的互动延伸 是 从基本的手写演示中得到 让用户在屏幕上 以 交互的方式编写一些字迹, 另一个 扩展是 可以建立类似 的distill.pub 。
使用此代码!
如果你是一个艺术家或有意于 机器学习的 设计师,你可以 据 自己的喜好使用 包含 这些 代码的GitHub 库。
这篇文章只从 表面分析了 循环神经网络。 还有其他的资源,比如 TensorFlow 或 kera s等。
更新:
该模型 已经被移植到bl.ocks 。
本文由北邮 @ 爱可可 - 爱生活 老师推荐, 阿里云云栖社区 组织翻译。
文章原标题《Recurrent Neural Network Tutorial for Artists 》,作者:hardmaru ,译者:海棠
文章为简译,更为详细的内容,请查看原文