Theano
Theano是什么
官方定义很简单:
Theano is a Python library that lets you to define, optimize, and evaluate mathematical expressions, especially ones with multi-dimensional arrays (numpy.ndarray).
其他翻译成汉语应该是:
Theano是一个Python库,专门用于定义、优化、求值数学表达式,效率高,适用于多维数组。类型深度学习库
Theano是一个Python库,允许您有效地定义,优化和评估涉及多维数组的数学表达式。Theano特色:
与NumPy紧密集成 - 在Theano编译的函数中使用numpy.ndarray。
透明使用GPU - 比CPU更快地执行数据密集型计算。
有效的象征性差异 - Theano通过一个或多个输入为您的函数做出衍生工具。
速度和稳定性优化 - log(1+x)即使x非常小,也能获得正确的答案。
动态C代码生成 - 更快地评估表达式。
广泛的单元测试和自我验证 - 检测和诊断多种类型的错误。
其他深度学习库
像⾕歌有⾃家的TensorFlow、Facebook有Torch、百度有Paddle Paddle、微软有CNTK、⽽亚⻢逊的AWS则有MXNet,现在还有⽀持HADOOP的NL4J深度学习框架。
为什么我们用theano?
tensowflow 目前只能在 MacOS 和 Linux, theano 不仅可以在前两个系统中运行, 还可以在 Windows 系统下运行;
theano 可以使用 GPU 进行运算,用GPU运行比CPU快100倍左右,theano 是比较优秀的 python 模块。
对于初学者来说,如果可以在 theano 和 tensorflow 中选择, 个人推荐使用 tensowflow. tensowflow 是比较商业化的模块,用起来没有theano 学术化。如果是使用机器学习进行学术性研究,网上已经有很多使用 theano 的学术性资料。所以 theano 在这种情况下是值得推荐的。
python
简单易⽤, 对数学矩阵向量等处理能⼒强,且库多,适⽤性⼴
NumPy
NumPy是Python中科学计算的基础软件包。它是一个提供多了维数组对象,多种派生对象(如:掩码数组、矩阵)以及用于快速操作数组的函数及API,它包括数学、逻辑、数组形状变换、排序、选择、I/O 、离散傅立叶变换、基本线性代数、基本统计运算、随机模拟等等。NumPy包的核心是ndarray对象。它封装了python原生的同数据类型的n维数组,为了保证其性能优良,其中有许多操作都是代码在本地进行编译后执行的。NumPy数组 和 标准Python Array(数组) 之间有区别。
NumPy 应用
NumPy 通常与 SciPy(Scientific Python)和 Matplotlib(绘图库)一起使用, 这种组合广泛用于替代 MatLab,是一个强大的科学计算环境,有助于我们通过 Python 学习数据科学或者机器学习。
SciPy 是一个开源的 Python 算法库和数学工具包。
SciPy 包含的模块有最优化、线性代数、积分、插值、特殊函数、快速傅里叶变换、信号处理和图像处理、常微分方程求解和其他科学与工程中常用的计算。
Matplotlib 是 Python 编程语言及其数值数学扩展包 NumPy 的可视化操作界面。它为利用通用的图形用户界面工具包,如 Tkinter, wxPython, Qt 或 GTK+ 向应用程序嵌入式绘图提供了应用程序接口(API)。
GPU和CPU
CPU即中央处理器GPU即图形处理器
两者的相同之处:两者都有总线和外界联系,有⾃⼰的缓存体系,以及数字和逻辑运算单元。⼀句话,两者都为了完成计算任务⽽设计。两者的区别:存在于⽚内的缓存体系和数字逻辑运算单元的结构差异:CPU虽然有多核,但总数没有超过两位数,每个核都有⾜够⼤的缓存和⾜够多的数字和逻辑运算单元,并辅助有很多加速分⽀判断甚⾄更复杂的逻辑判断的硬件;GPU的核数远超CPU,被称为众核(NVIDIA Fermi有512个核)。 简⽽⾔之,当程序员为CPU编写程序时,他们倾向于利⽤复杂的逻辑结构优化算法从⽽减少计算任务的运⾏时间,即Latency。当程序员为GPU编写程序时,则利⽤其处理海量数据的优势,通过提⾼总的数据吞吐量(Throughput)来掩盖Lantency。⽬前,CPU和GPU的区别正在逐渐缩⼩,因为GPU也在处理不规则任务和线程间通信⽅⾯有了⻓⾜的进步。另外,功耗问题对于GPU⽐CPU更严重
GPU已经不再局限于3D图形处理了,GPU通⽤计算技术发展已经引起业界不少的关注,事实也证明在浮点运算、并⾏计算等部分计算⽅⾯,GPU 可以提供数⼗倍乃⾄于上百倍于CPU的性能,如此强悍的“新星”难免会让CPU⼚商⽼⼤英特尔为未来⽽紧张,NVIDIA和英特尔也经常为CPU和GPU 谁更重要⽽展开⼝⽔战。
安装要求
python 2 >=2.6 or python 3>=3.3
Numpy >= 1.7.1
Scipy >= 0.11
通用安装法
直接复制粘贴下面的命令, 然后在 terminal 当中运行. 如果要使用 GPU 加速运算, 要确保在你的电脑上有 NVIDIA 的 GPU 显卡, 而且能够使用 CUDA 模块.
# python 2+ 版本
pip install theano
# python 3+ 版本
pip3 install theano
如果遇到权限问题, 请在 pip 前加一个 sudo 如: sudo pip install theano.
更详细的Theano安装步骤:
Theano基本用法
在 theano 中学会定义矩阵 matrix 和功能 function 是一个比较重要的事, 我们在这里简单的提及了一下在 theano 将要运用到的东西.
在Theano中所有变量都被处理为张量(Tensor),比如:标量 scalar、向量 vector、矩阵 matrix。
tensor就是数学上的张量,根据维度(dimension)数目不同,tensor variable分为scalar、vector、matrix、tensor3、tensor4等具体类型,他们的dimension数目分别为0、1、2、3、4。
如下定义一个scalar,也就是标量:
importtheano.tensorasTT
x=TT.scalar('x',dtype='float32')
y=TT.fscalar('y')# f表示float
我们定义了两个标量tensor variable,x和y的数据类型均为float;单引号中的x和y表示名字,仅供调试方便,可以省略。vector,matrix,tensor3等地定义以此类推。
theano 和 tensorflow 类似,都是基于建立神经网络每个组件,在组件联系起来,数据放入组件,得到结果。
首先, 我们这次需要加载 theano 和 numpy 两个模块, 并且使用 theano 来创建 function.
import numpy as np
import theano.tensor as T
from theano import function
定义X和Y两个常量 (scalar),把结构建立好之后,把结构放在function,在把数据放在function。
# basic
x = T.dscalar('x') # 建立 x 的容器
y = T.dscalar('y') # 建立 y 的容器
z = x+y # 建立方程
# 使用 function 定义 theano 的方程,
# 将输入值 x, y 放在 [] 里, 输出值 z 放在后面
f = function([x, y], z)
print(f(2,3)) # 将确切的 x, y 值放入方程中
# 5.0
使用 theano 中 的 pp (pretty-print) 能够打印出原始方程:
from theano import pp
print(pp(z))
# (x + y)
定义矩阵,以及利用矩阵做相关运算:
x = T.dmatrix('x') # 矩阵 x 的容器
y = T.dmatrix('y') # 矩阵 y 的容器
z = x + y # 定义矩阵加法
f = function([x, y], z) # 定义方程
print(f(
np.arange(12).reshape((3,4)),
10*np.ones((3,4))
)
)
"""
[[ 10. 11. 12. 13.]
[ 14. 15. 16. 17.]
[ 18. 19. 20. 21.]]
"""
Function 用法
注:theano 当中的 function 就和 python 中的 function 类似, 不过因为要被用在多进程并行运算中,所以他的 function 有他自己的一套使用方式.
这是涉及的是Theano 的function 用法。在theano中由于涉及到GPU加速以及CPU 的并行的运算,所以他的function会有不同。
这次介绍了function的三种用法,并且各举一个例.
首先 , 导入所需要的python包:
import numpy as np
import theano.tensor as T
import theano
激励函数例子
使用activation function(激励函数)的例子。 activation function 的例子是使用 function 最简单的形式。 首先需要定义一个 tensor T:
x = T.dmatrix('x')
然后声明了概率计算方式,这里需要注意这里的计算方式要用到Theano里面的计算方式。而不能使用numpy包里面的exp()。
s = 1/(1+T.exp(-x)) # logistic or soft step
最后。调用 theano 定义的计算函数 logistic
logistic = theano.function([x], s)
print(logistic([[0,1],[-2,-3]]))
"""
[[ 0.5 0.73105858]
[ 0.26894142 0.11920292]]
"""
多输入/输出的 function
假定我们的 theano 函数中的输入值是两个,输出也是两个。 指定输入的值是矩阵a,b
a,b = T.dmatrices('a','b')
计算输入a,b 之间的差(diff), 差的绝对值(abs_diff),差的平方(diff_squared)
diff=a-b
abs_diff=abs(diff)
diff_squared = diff**2
在这次使用theano.function的时候可以指定两个输,并且输出这两个数值的差(diff),差的绝对值(abs_diff), 差的平方(diff_squared)。当我们在调用这个函数的时候会将这三个结果作为输出值。
f = theano.function([a,b], [diff,abs_diff,diff_squared])
最后调用函数f, 并且向函数传递初始化之后的参数。
x1,x2,x3= f(
np.ones((2,2)), # a
np.arange(4).reshape((2,2)) # b
)
print(x1, x2, x3)
"""
array([[ 1., 0.],
[-1., -2.]]),
array([[ 1., 0.],
[ 1., 2.]]),
array([[ 1., 0.],
[ 1., 4.]])
"""
function 的名字
首先,我们可以使用 T.dscalars() 里面同时定义三个纯量的容器。 以及输出值z
x,y,w = T.dscalars('x','y','w')
z = (x+y)*w
接下来应该是定义 theano 的函数了, 在定义函数的并且指定输入值的时候,我们期望能够有默认值, 于是我们使用 theano 的默认值书写方式来指定
# name for a function
f = theano.function([x,
theano.In(y, value=1),
theano.In(w,value=2)],
z)
print(f(23)) # 使用默认
print(f(23,1,4)) # 不使用默认
"""
48.0
100.0
"""
同时,我们还可以在定义默认值的时候,可以指定参数名字。 这样做的目的是防止我们定义的参数过于多的情况下,忘记函数的顺序。
f = theano.function([x,
theano.In(y, value=1),
theano.In(w,value=2,name='weights')],
z)
print (f(23,1,weights=4)) ##调用方式
"""
100.0
"""
总结
这节中,我们介绍了function的三种方式: 首先,一个theano的function的简单用法; 其次在使用theano的function中可以有多个input和output; 最后是theano的function中可以有默认值并且可以给参数指定名称。在 fucntion 的参数会在CPU/GPU中共享
Shared 变量
shared variable这也是一个tensor variable,名字虽然叫shared,但这不是它的本质,它本质上是一块稳定存在的存储空间,类似于全局变量,但与全局变量不同的是,它可以存储在显存中,很多场景下GPU加速杠杠的。一个重要的语法特性是,Theano函数可以在每次执行时顺便修改shared variable的值。比如用shared variable实现累加操作
定义 Shared 变量 我们会用累加器来定义 Shared 变量,每一次向上面加一个值,每一次基于上面的变化,再加上另一个值,就这样不断地更新并保存这样的值。
state = shared(0)
inc = T.iscalar('inc')
accumulator = function([inc], state, updates=[(state, state+inc)])
>>> print(state.get_value()) # 打印state的初始值
0
>>> accumulator(1) # 进行一次函数调用
array(0)
>>> print(state.get_value()) # 函数返回后,state的值发生了变化
1
state是一个shared variable,初始化为0,每次调用accumulator(),state都会加上inc。shared variable可以像普通tensor variable一样用于符号表达式,另外,他还有自己的值,可以直接用.get_value()和.set_value()方法来访问和修改。
上述代码还引入了一个东西:updates。这是用theano函数操作shared variable的方法,参数是一个list,list的每个元素都是二元tuple,tuple首元素是要修改的shared,第二个元素是更新后的值。updatas中的share variable会在函数返回后更新自己的值。
updates表面上是一种语法糖,但它真正的作用在于执行效率,updates多数时候可以用in-place的算法快速实现,在GPU上,theano可以更好地控制何时何地给shared variable分配空间,带来性能提升。最常见的,神经网络里权值的更新一般会用update实现。
求导
theano.tensor.grad(output, input)可以求表达式导数,第一个参数是要求导的表达式,第二个参数是自变量,或者自变量组成的list。
import theano
import theano.tensor as TT
x = TT.dscalar('x')
y = x ** 2 # 定义函数y=x^2
gy = T.grad(y, x) # 导函数的表达式
f = theano.function([x], gy) # 将导函数编译成Theano函数
这样就得到了函数f(x)=x^2的导数,然后测试一下:
>>> theano.pp(gy) # 打印优化之前的导函数表达式
((fill((x ** TensorConstant{2}), TensorConstant{1.0}) * TensorConstant{2}) * (x ** (TensorConstant{2} - TensorConstant{1})))
>>> f(4) # 带入数据计算x=4时的导数值
array(8.0)
>>> pp(f.maker.fgraph.outputs[0]) # 打印编译优化后的导函数表达式
(2.0 * x)
求解结果f'(4)=8,与预期相符。pp(gy)输出grad的结果,是未经优化的,有多个节点,编译后的函数f则是优化过的,只有一个节点,这也反映出,函数编译中的优化是很重要的。
Regression 回归例子
导入模板
引入需要使用的Python包:
import theano
import theano.tensor as T
import numpy as np
import matplotlib.pyplot as plt #引入了matplotlib这个工具包, 用来实现绘图及数据可视化。
定义层结构
接下来我们声明我们的Layer类。 对于神经网络的每个Layer, 它需要具备输入来源input,输入神经元维度in_size,输出神经元纬度out_size, 和我们之前设计的神经元的激活函数activation_function, 默认为None。
class Layer(object):
def __init__(self, inputs, in_size, out_size, activation_function=None):
self.W = theano.shared(np.random.normal(0, 1, (in_size, out_size)))
self.b = theano.shared(np.zeros((out_size, )) + 0.1)
self.Wx_plus_b = T.dot(inputs, self.W) + self.b
self.activation_function = activation_function
if activation_function is None:
self.outputs = self.Wx_plus_b
else:
self.outputs = self.activation_function(self.Wx_plus_b)
伪造数据
接下来,我们首先人工生成一个简单的带有白噪声的一维数据 y = x^2 - 0.5 + noise。
# Make up some fake data
x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
y_data = np.square(x_data) - 0.5 + noise # y = x^2 - 0.5 + wihtenoise
然后,我们用散点图(Scatter)把它显示出来, 横轴是x_data, 纵轴是y_data。
# show the fake data
plt.scatter(x_data, y_data)
plt.show()
显示结果:
搭建网络
然后,我们定义神经网络的输入与目标;
# determine the inputs dtype
x = T.dmatrix("x")
y = T.dmatrix("y")
这里,T.dmatrix 意味着我使用的是float64的数据类型, 和我的输入数据一致。
接着我们设计我们的神经网络,它包括两层,构成1-10-1的结构。 对于l1我们的input_size要和我们的x_data一致,然后我们假设了该层有10个神经元,并且以relu作为激活函数。 所以,l2以l1.output为输入,同时呢,它的输出为1维,和我们的y_data保持一致,作为神经网络的输出层,我们采用默认的线性激活函数。
# determine the inputs dtype
# add layers
l1 = Layer(x, 1, 10, T.nnet.relu)
l2 = Layer(l1.outputs, 10, 1, None)
然后,我们定义一个cost,也就是损失函数(cost/loss function),我们采用的是l2.outputs 神经网络输出与目标值y的的平均平方差。
# compute the cost
cost = T.mean(T.square(l2.outputs - y))
根据 cost 我们可以计算我们神经网络权值和偏置值的梯度(gradient), 这里Theano已经集成好了对应的函数:
# compute the gradients
gW1, gb1, gW2, gb2 = T.grad(cost, [l1.W, l1.b, l2.W, l2.b])
有了以上的基本运算步骤后,我们就可以开始,利用梯度下降法训练我们的神经网络。 首先我们定义一个学习率 learning_rate, 这个学习率的取值一般是根据数据及实验的经验估计的,它会对模型的收敛性造成一定的影响,一般倾向于采用较小的数值。
然后,我们定义train这个函数来描述我们的训练过程,首先我们定义了函数的输入inputs=[x, y], 函数的输出为outputs=cost, 同时更新网络的参数。
# apply gradient descent
learning_rate = 0.05
train = theano.function(
inputs=[x, y],
outputs=cost,
updates=[(l1.W, l1.W - learning_rate * gW1),
(l1.b, l1.b - learning_rate * gb1),
(l2.W, l2.W - learning_rate * gW2),
(l2.b, l2.b - learning_rate * gb2)])
然后我们定义一个预测函数来输出我们最终的结果predict.
# prediction
predict = theano.function(inputs=[x], outputs=l2.outputs)
训练
最后,我们就要开始真正的训练啦!我们要把神经网络训练1000次,同时每训练50次时就输出此时的误差(cost):
for i in range(1000):
# training
err = train(x_data, y_data)
if i % 50 == 0:
print(err)
你大概会得到一下类似的结果:
"""
2.2858426513500967
0.011148671551881364
0.008641346716060181
0.007188403510180637
0.006250296000631469
0.005628021126864056
0.005142288453451058
0.004793442944984919
0.004539827398288326
0.004376693858775578
0.0042555015795511615
0.004156078781653181
0.0040801312408181726
0.004022424092526545
0.003974514689028584
0.003934815285052449
0.0039030377541023824
0.003875222239897254
"""
结果显示我们的训练误差是在不断的减小的,也就是说我们的神经网络在一点一点的逼近目标值。
相关链接
网友评论