Theano介绍及简单应用

来源:互联网 发布:数据库中union all 编辑:程序博客网 时间:2024/06/13 22:40

Theano介绍及简单应用

一、课程介绍

1.课程说明

本次课程主要是为引导大家入门几个常用的深度学习框架。在课程中有关各种算法的详细推导及思想我们不会做过多的陈诉,旨在通过让同学们动手操作搭建自己的神经网络模型,了解如何使用这些框架解决实际问题。

课程中我们会基于每个框架的官方文档,先带大家熟悉框架中基本的定义语法及常用函数(可看成深度模型的小零件),每个实验最后都会综合这些零件搭建一个简易的神经深度网络模型。学习过程中有问题可随时指出。

2.深度学习概念

深度学习 (deep learning):深度学习是机器学习中的一个分支,试图通过具有多个处理层的计算模型对数据进行多层抽象。这个抽象的结果即是数据一种表达,而深度学习也可看成表达学习(表征学习)的一种方法,深度学习的好处是用无监督式 Unsupervised或半监督式 Semi-supervised的特征学习和分层特征提取高效算法来替代手工获取数据特征。

简单来说,深度学习可以理解为具有很多层(>2)的人工神经网络模型算法,神经网络的构建其实也可以看成一个点-线-面的构造过程。:即神经网络里许多计算节点(在神经网络里,称为神经元 Cell),线:由一些节点再构成一层处理层(这一层就可以理解为对数据的某个抽象表达),:由多个处理层构建起来就形成了一个神经网络。

再由点(神经元,Cell)不同,或是这些点的连接方式不同就可以形成各种各样的不同的表达模型。如下图所示,我们可以看到许多种神经网络结构,例如:Deep Feed Forward深度前向传播网络Recurrent Neural Network(RNN)递归神经网络Long/Short Term Memory(LSTM)长短期记忆神经网络

此处输入图片的描述

图片来自:THE ASIMOV INSTITUTE

3.本节内容

本次实验所包含的实验流程如下:

  1. Theano介绍及安装
  2. Theano基本使用
  3. Theano实现stacked Autoencoder(栈式自编码器)

4.实验相关知识点

在后续的课程中,需要预先有了解的知识点:

  • Tensor: 张量,经典的方法把张量视为多维数组,它们是标量,1维向量和2维矩阵的n维推广。张量的"分量"是数组中的值。 这个思想可以进一步推广到张量场,那里张量的元素是函数,甚至微分。 —— 引自维基百科
  • GPU:graphics processing unit,图形处理器

  • Numpy:python中有关数值计算的库

    • Matrix conventions (矩阵约定) for machine learning :矩阵中的每行都是一个例子。如 一个10行5列的矩阵inputs[10,5], 代表着10个5维的实例。

    • Broadcasting: Numpy进行数值运算时广播不同维度的矩阵。如,当一个较小的矩阵a(1*212维)与一个较大的矩阵b( 2*323 维)进行加法运算时,numpy会将矩阵a广播为与矩阵b兼容的矩阵维度,再进行运算。也可通过下图帮助理解。

    此处输入图片的描述

二、Theano介绍及安装

Theano是一个较为老牌和稳定的机器学习python库之一。Theano基于Python擅长处理多维数组(紧密集成了Numpy),属于比较底层的框架,theano起初也是为了深度学习中大规模人工神经网络算法的运算所设计,我们可利用符号化式语言定义想要的结果,接着theano会对我们的程序进行编译,使其高效运行于GPUCPU

从Theano官方文档的欢迎页面上,我们可以看到其对Theano的特征介绍:

  • tight integration with Numpy (紧密集成Numpy) —— 在Theano编译函数中使用numpy.ndarray
  • transparent use of a GPU (GPU的透明使用) —— 使得其对浮点数的运输非常高速
  • efficient symbolic differentiation (高效的符号分解) —— 也是Theano的发明初衷之一,可帮我们推导我们所定义的一个或多个输入的函数
  • speed and stability optimizations (快速且稳定的优化) —— 在函数表达式$log(1+x)$中即使$x$非常小,也可以给出精确的答案
  • dynamic C code generation (动态生成C代码) —— 使得表达式求值(evaluate expression)更加快速
  • extensive unit-testing and self-verification (大量的单元测试及自我验证) —— 可检测及诊断许多类型的错误

在Installing Theano中可查看在各种系统上安装Theano的教程。这里我们将展示在实验楼的ubantu14.04环境的安装过程:

# 1. 先使用apt-get update命令进行更新,避免安装Python库时出现问题$ sudo apt-get update# 2. 接着安装在安装theano前需安装的Python库$ sudo apt-get install python-numpy python-scipy python-matplotlib  python-dev python-pip python-nose python-tk g++ libopenblas-dev git# 3. 接着第三步便是使用pip命令安装theano$ sudo pip install theano

此处输入图片的描述

三、Theano基本使用

3.1 Theano的基本运算及符号表达

通过python进入Python终端中,先导入theano的相关模块以便后续操作:

>>> import theano>>> import theano.tensor as T>>> from theano.tensor import *>>> from theano import shared>>> import numpy as np

此处输入图片的描述

基本的张量功能:

Theano 支持Python中任何类型的对象,但它的核心是其支持符号矩阵表达式,如下例中:

>>> x = T.fmatrix()

x是一个TensorVariable张量变量的实例,T.fmatrix对象本身也是TensorType张量类型的一个实例。

Creation创造张量实例

Theano提供了一列预先定义好的张量类型可供使用者很方便地创造张量变量,所有的张量构造函数都接收一个 可选 的输入参数name。如以下便构造了三个以myvar为名字的 0维整型标量(scalar)变量

>>> x = T.scalar('myvar', dtype='int32')>>> x = iscalar('myvar')>>> x = TensorType(dtype='int32', broadcastable=())('myvar')

还可以通过vectorrowcolmatrixtensor3tensor4分别构造向量行向量列向量矩阵三维张量四维张量

我们还可以通过以下方式创造张量类型实例

# 创造一个双精度浮点类型的无名矩阵>>> x = dmatrix()  # 创造了一个双精度浮点类型的名为'x'的矩阵>>> x = dmatrix('x')# 创造了一个双精度浮点类型的名为'xyz'矩阵>>> xyz = dmatrix('xyz')

此处输入图片的描述

Plural Constructor 多输出构造函数

Plural Constructor可以同时构造多个变量,以下便是Theano提供的多输出构造函数,其实就是intlong, floatdouble数据类型与标量scalars, 向量vectors行向量rows, 列向量cols矩阵matrices的组合:

  • 返回一个或多个标量:iscalarslscalarsfscalarsdscalars
  • 返回一个或多个向量:ivectorslvectorsfvectorsdvectors
  • 返回一个或多个行变量:irowslrows , frowsdrows
  • 返回一个或多个列变量:icolslcolsfcolsdcols
  • 返回一个或多个矩阵: imatriceslmatricesfmatricesdmatrices

举一个'栗子':

# 构造三个无名双精度浮点类型矩阵>>> x, y, z = dmatrices(3)# 构造三个分别名为'x','y','z'的double类型矩阵>>> x, y, z = dmatrices('x', 'y', 'z')

此处输入图片的描述

依次类推,Theano中还有其他的张量类型,除前边的章节介绍外的还有字节型byte字型word复数型complex的数据形成的张量。

自定义张量类型

如果上述所提供的构造函数不能构造出你想要的张量时,你可以自定义自己的张量类型TensorType。比如,构造一个5维张量

>>> dtensor5 = TensorType('float64', (False,)*5)>>> x = dtensor5()>>> z = dtensor5('z')

此处输入图片的描述

也可以重定义已经提供的张量构造函数。如重构一个 双精度浮点类型矩阵

>>> my_dmatrix = TensorType('float64', (False,)*2)>>> x = my_dmatrix() #构造一个无名2维矩阵实例## 判断自定义的张量类型是否与theano库所提供的构造函数一致>>> my_dmatrix == dmatrix

此处输入图片的描述

3.2 Theano中的线性运算

在进行线性运算前,先从theano中导入一个新的函数function

>>> from theano import function

使用Theano进行线性运算过程就跟把大象放进冰箱的步骤差不多:

  1. 打开冰箱——定义好需要进行运算的张量变量
  2. 把大象放进冰箱——利用已定义的张量书写你的表达式
  3. 关上冰箱——将表达式转化为可执行的python函数

基于以上三个步骤我们实现两个张量的加运算:

# 1. 定义变量,可以是3.1节介绍的张量类型中的任一种,这里我定义的是double scalar双精度标量>>> x = T.dscalar('x')>>> y = T.dscalar('y')# 或者你可以直接同时定义这两个变量>>> x, y = T.dscalars('x', 'y')# 2. 表达式>>> z = x + y# 3. 将表达式转化为可执行的python函数>>> f = function([x, y], z)

此处输入图片的描述

第3个步骤中的function函数,其接受的第一个参数类型为 a list of Variables 一组输入变量 ,将作为转化后的python函数f的输入,第二个参数类型为 a single Variable or a list of variables一个或一组变量,将作为函数f的输出。

接下来我们就可以使用函数f进行表达式运算

>>> f(2.33, 6.66)

此处输入图片的描述

如果你想在函数f中只个一个输入,而执行加运算。那么可以设置其中一个变量有缺省值 Default Value

>>> from theano import In>>> f = function([x, In(y, value=1)], z) # In理解为Instance实例>>> f(1.33)

此处输入图片的描述

还有些时候,我们会在许多函数间用到同一个变量值,那么此时我们可以定义一个共享变量(shared Variable), 共享变量由使用其计算的函数所共享,可通过shared_constructor()构造,可通过get_valueset_value函数分别获得和改变共享变量的值。

>>> np_array = np.zeros(2, dtype='float32')# 创建共享变量>>> shared_default = shared(np_array)>>> shared_false = shared(np_array, borrow=False)>>> shared_true = shared(np_array, borrow=True)

此处输入图片的描述

shared函数中的参数borrow的含义是,变量np_array在函数中被使用时所创建共享变量shared_xxx,在np_array的值发生改变时共享变量是否要跟着发生变化,同学们可通过运行下边的代码获得答案:

>>> np_array += 1 #在数组np_array[0, 0]的值上加1>>> shared_default.get_value()>>> shared_false.get_value()>>> shared_true.get_value()

此处输入图片的描述

3.3 练习

经过上边的介绍,我们来实现以下表达式:

  1. 向量x的表达式x+x^{10}x+x10
  2. 矩阵x的表达式 s(x)=\frac{1}{1+e^{-x}}s(x)=1+ex1
  3. 基于表达式2实现s(x)s(x)的导数形式
# 表达式1>>> x = T.vector()>>> y = x + x ** 10>>> f = function([x], y)>>> f([0, 1, 2])

此处输入图片的描述

# 表达式2>>> x = T.dmatrix('x')>>> s = 1 / (1 + T.exp(-x))>>> logistic = function([x], s)>>> logistic([[0 , 1], [-1, -2]])

此处输入图片的描述

#表达式3>>> s = T.sum(1 / (1 + T.exp(-x)))>>> gradS = T.grad(s, x)>>> dLogistic = function([x], gradS)>>> dLogistic([[0, 1], [-1, -2]])

此处输入图片的描述

结束之后,利用Ctrl + D退出python终端

四、Theano实现自编码器

本节我们将利用theano实现第一节的关于人工神经网络附图中的自编码器(AutoEncoder, AE):

此处输入图片的描述

上图中的自编码器为最简单的三层‘深度’神经网络,在实验楼的课程基于无监督学习的自编码器实现中,我们已经对自编码器解释得比较详细,这里不做过多解释,直接动手做实验:

4.1定义自编码器类

创建AE_code.py文件,在其中定义自编码器类方便我们在之后时使用这个三层前向传播神经网络,对我们给定的数据进行自编码器的训练。

  1. 导入相关的python模块:
# -*- coding: utf-8 -*-import numpy as npimport theano as thfrom theano import tensor as Tfrom numpy import random as rng
  1. 创建AutoEncoder类:
class AutoEncoder(object):    # 在初始化函数中定义自编码器的参数    def __init__(self, data, hidden_size, actFun, outFun):        self.data = data        self.data = th.shared(name='data', value=np.asarray(self.data, dtype=th.config.floatX),                             borrow=True)        self.nIns = data.shape[0]        self.nFeatures = data.shape[1]        self.hidden_size = hidden_size        # W初始值的取值范围,在一个固定范围中取均匀分布的随机值        raw_W = np.asarray(random.uniform(            low = -4 * np.sqrt(6. / (self.hidden_size + self.nFeatures)),            high = 4 * np.sqrt(6. / (self.hidden_size + self.nFeatures)),            size=(self.nFeatures, self.hidden_size)), dtype=th.config.floatX)        self.W = th.shared(value=raw_W, name='W', borrow=True)        # 隐含层的偏置向量        self.b1 = th.shared(name='b1', value=np.zeros(shape=(self.hidden_size,),                                                      dtype=th.config.floatX), borrow=True)        # 输出层的偏置向量        self.b2 = th.shared(name='b2', value= np.zeros(shape=(self.nFeatures,),                                                       dtype=th.config.floatX), borrow=True)        self.actFun = actFun        self.outFun = outFun

通过上边的代码可知,在自编码器的权值初始化时是在区间[low, high]上取得,其中low和high的值分别为:

low = - 4 * \sqrt{\frac{6}{n_{hidden}+n_{visible}}}low=4nhidden+nvisible6

high = 4 * \sqrt{\frac{6}{n_{hidden}+n_{visible}}}high=4nhidden+nvisible6

而自编码器的隐含层输出层偏置都设置为零向量

  1. 自编码器的训练
# 定义自编码器的训练函数# 训练时的参数在这里有默认设置值,在实际调用时可根据输入数据进行修改    def train(self, n_epochs=100, mini_batch_size=10, learning_rate=0.1):        index = T.lscalar()        x = T.matrix('x')        params = [self.W, self.b1, self.b2]        hidden = self.actFun(T.dot(x, self.W) + self.b1)        output = T.dot(hidden, T.transpose(self.W)) + self.b2        output = self.outFun(output)        # cross-entropy loss        L = -T.sum(x * T.log(output) + (1 - x) * T.log(1 - output), axis=1)        cost = L.mean()        updates = []        # gradient with respect to W, b1, b2        gparams = T.grad(cost, params)        for param, gparam in zip(params, gparams):            updates.append((param, param - learning_rate * gparam))        # training given a mini-batch of the data        train = th.function(inputs=[index], outputs=[cost], updates=updates,                            givens={x: self.data[index:index + mini_batch_size, :]})        import time        start_time = time.clock()        for epoch in xrange(n_epochs):            print "Epoch:", epoch            for row in xrange(0, self.nIns, mini_batch_size):                train(row)            end_time = time.clock()            print "Average time per epoch=", (end_time - start_time) /n_epochs
  1. 定义自编码器输出结果的获取
                def get_output(self, data):        W = self.W.get_value()        b1 = self.b1.get_value()        b2 = self.b2.get_value()        x = T.dmatrix('x')        hidden = self.actFun(T.dot(x, W) + b1)        output = T.dot(hidden, W.T) + b2        output = self.outFun(output)        ae_output = th.function(inputs=[x], outputs=[output])        return ae_output(data)

4.2 调用自编码器类训练minist手写体的自编码网络

本次实验所用的数据集,与我们在课程基于无监督学习的自编码器实现所用的数据集一样,为方便大家在实验楼的在线环境中进行实验,在minist数据集中选用一部分并且已经读入到trainData.mat文件中,节省文件读取的时间。

trainData.mat可通过wget命令从以下地址获得:

wget http://labfile.oss.aliyuncs.com/courses/696/trainData.mat

实验代码部分:

import matplotlib.pyplot as pltdef main():    trainData = scio.loadmat('trainData.mat')    inputData = trainData['trainData'].T    inputData = inputData[:,:] / 255.    activation_function = T.nnet.sigmoid    output_function = activation_function    A = AutoEncoder(inputData, 200, activation_function, output_function)    # 训练自编码器,只训练一个epoch,在训练过程中每次只通过一张图片进行权值改进    A.train(1, 1)    # 训练集中每个数字有对应50张训练图片    eachDigitNum = 50    showIndex = range(0, inputData.shape[0], eachDigitNum)    # Autoencoder类中的get_output函数返回的是一个list    # 因此需通过下标0,将list中的结果矩阵读出    output = A.get_output(inputData[showIndex, :])[0]    # 展示原始图片及自编码器输出结果    for iImg in xrange(10):        ax = plt.subplot(2, 10, iImg + 1 )        plt.imshow(inputData[showIndex[iImg],:].reshape(28, 28).T, cmap= plt.cm.gray)        ax.get_xaxis().set_visible(False)        ax.get_yaxis().set_visible(False)        ax = plt.subplot(2, 10, iImg + 11 )        plt.imshow(output[iImg, :].reshape(28, 28).T, cmap=plt.cm.gray)        ax.get_xaxis().set_visible(False)        ax.get_yaxis().set_visible(False)    plt.show()if __name__ == '__main__':    main()

最终通过命令行运行AE_code.py文件,查看自编码训练结果

python AE_code.py

程序运行结果截图:

此处输入图片的描述

此处输入图片的描述

五、总结

通过本次课程,我们学习了Theano中的基本使用方法,了解了Theano的一些基本特性及不同的数据类型。通过实验了解常用的数据符号定义方法,及如何通过theano实现数据表达式。本节实验最后我们Theano实现了一个三层FeedForward network 前馈网络Theano还可实现许多人工神经网络,同学们可自己动手实现。

本次课程的所有源码,可通过以下命令获得:

wget http://labfile.oss.aliyuncs.com/courses/744/AE_code.py

六、参考阅读

  • 利用Theano理解深度学习——多层感知机
  • 利用Theano理解深度学习——自编码器实现
  • 深度学习算法实践——用Theano实现多层前馈网络

七、本节习题

在本次实验中,我们实现了Autoencoder类中的get_output函数,来查看自编码器的输出结果。同学们可通过动手自定义get_hidden函数,获得编码hidden层的编码结果,并在main函数中调用查看网络训练结束后的对每个图片的编码情况。