浅尝TensorFlow
什么是深度神经网络(DNN)?什么是TensorFlow?TensorFlow能干啥?怎么在JAVA中使用TensorFlow?
什么是TensorFlow
TensorFlow 是由 Google Brain 团队为深度神经网络(DNN)开发的开源软件库。
什么是神经网络
神经网络也称为连接模型。它的设计来源于人脑的神经元-突触,神经网络就是大量被称为权重的‘突触’相互连接的人造神经元的集合。人造神经网络通训练数据集进行训练,在训练了足够数量的训练数据后,人造神经网络可以提取数据信息特征,用于识别它们没有见过的数据。神经网络属于机器学习中的一部分。
什么是深度神经网络(DNN)
神经网络由神经元构成,,这些神经元以分层的模式叠加。只有一层神经元的神经网络是感知机,而DNN是至少一个神经元层的神经网络:可以理解为有很多隐藏层的神经网络,也称为多层感知机。
TensorFlow库可以进行大规模的数值计算,如矩阵乘法或自动微分,这些是深度学习的基础。而且深度学习的四个重要部分组成:数据集、定义模型(网络结构)、训练/学习和预测/评估都可以在 TensorFlow 中实现。并且TensorFlow可以输出学习过程中可视化的摘要。神经网络的实现略有点复杂,需要很多的数学基础,我们先绕过他,通过实现一个线性回归模型来初步学习怎么使用TensorFlow。
什么是线性回归
回归是功能非常强大的工具之一,可以应用到工程、物理学、生物学、金融、社会科学等各个领域进行数学建模、分类和预测中,是数据分析常用的基本工具。而线性回归则假定输入变量(X)和单个输出变量(Y)之间呈线性关系。线性回归的建模过程旨在找到预测值 Y 的线性方程:Y = W * X + b。这里的W(权重)和b(偏置项)可以通过训练逼近实际的数值。
使用TensorFlow
TensorFlow一开始只是基于Python的,所以在Java下的应用还不是很成熟,如果你会用Python,别犹豫,关掉这篇文章自己去找Python的教程吧!
将TensorFlow引入项目
我们用gradled管理项目依赖包:
implementation group: 'org.tensorflow', name: 'tensorflow-core-api', version: '0.2.0'
implementation group: 'org.tensorflow', name: 'tensorflow-framework', version: '0.2.0'
implementation group: 'org.tensorflow', name: 'tensorflow-core-api', version: '0.2.0', classifier: 'windows-x86_64'
在引入tensorflow-core-api的时候需要指定classifier:代码将要运行的操作系统。
而关于版本号,可以在tensorflow/java: Java bindings for TensorFlow (github.com) 看到,官方建议下载源代码,自己构建jar包是最好的(当然这可能会很复杂)。
构造训练数据
预定义一个简单的线性回归方程来产生训练数据(当然在实际应用中,回归方程的系数都是未知的)。
1 | int random = new Random(); |
初始化TensorFlow计算图
TensorFlow数据和模型都构建在一个Graph(图)中。
1 | Graph graph = new Graph() |
TensorFlow中的图相当于是一个画板,我们需要往上面画上我们的线性回归的模型,首先获取上下文对象(呃可以看成画笔)。
1 | Ops tf = Ops.create(graph); |
构造模型输入
获取到画笔后,我们就可以在画板上绘制模型的结构了。我们的线性回归模型,需要有一个输入,一个输出:
1 | Placeholder<TFloat32> xData = tf.placeholder(TFloat32.DTYPE, Placeholder.shape(Shape.scalar())); |
让我们看一下这段代码中用的类:
Placeholder:Placeholder对象是TensorFlow中的占位符,用来定义输入输出节点。
Shape:这是一个定义数据结构的对象,用来定义模型中数据。要了解这个对象是干嘛用的,首先我们要了解在神经网络中,数据都是矩阵的结构,比如一个4*3(4行3列)的矩阵是这样的:
那么用Shape表示的话就是Shape.of(4,3)
。而在我们代码中定义的输入输出都只是一个单独的变量,不构成复杂的矩阵,就可以用Shape.scalar()
来表示。
定义模型中的变量
模型中可以通过训练改变的两个变量是权值w(和输入值相关)和偏置项b(和输入值无关)。
1 | Variable<TFloat32> weight = tf.variable(tf.constant(1f)); |
Variable对象用来表示可变的数值(变量)。代码里面我们用到一个值为1的TensorFlow常量:tf.constant(1f),常量是指数值在计算过程中不能改变。这里的初始化为1的常量是用来对两个变量(weight ,bias)进行初始化用的。
定义线性回归的计算模型
我们需要在TensorFlow图上用代码的方式定义出线性回归计算模型(x*w +b)
,需要使用到TensorFlow库中预定义的各种数学函数(我们不能直接使用 +-*/ 这样的Java运算符号,不过在Python中是可以直接使用运算符)。
//Mul是乘法
Mul
//Add是加法
Add
//这里的y_就是我们的计算模型的实际输出
到这里,我们一定要在心里牢记,我们现在只是定义了一个初始模型,相当于一个框架,这个模型中的数值都还没有意义,需要后续通过训练才能变成真正的线性回归计算模型。
定义损失函数
损失函数是用来计算评估模型的实际输出和期待输出的差距,通过损失函数来评估训练的结果并且为后续通过训练函数调整变量提供依据。对于线性回归,最常用的是最小二乘法:
在代码中我们需要简化一下这个公式(正常的损失函数需要对所有训练样本的误差求和,但是在这里我们为了节省内存空间,不存储训练过程中的所有输出,所以不做求和操作),实现如下:
1 | Square<TFloat32> sum = tf.math.square(tf.math.sub(yPredicted, yData)); |
sub:减法操作
square: 平方操作
div:除法操作
constant定义了一个常量,这个常量的值是训练数据量的两倍。
训练函数
训练函数是在训练过程中根据损失函数,不断进行变量调整,是的损失函数往最小逼近。训练函数的选择和设计会涉及到很多数学方面的计算,但是这里我们不去学习这些东西,我们直接选择TensorFlow中预定义的的选择梯度下降优化器来训练我们的模型,这个梯度下降算法也是神经网络训练中很常用的一个训练算法。
1 | Optimizer optimizer = new GradientDescent(graph, 0.1f); |
开始训练
首先要创建一个会话对象Session并且进行初始化。
1 | Session session = new Session(graph) |
然后将我们准备好的训数据输入到模型中进行训练。用一个for循环轮询所有数据:
1 | for (int i = 0; i < x.length; i++) { |
List<Tensor<?>> tensorList = session.runner()
.fetch(w)
.fetch(b)
.run();
try (Tensor
Tensor
System.out.println(“Weight is “ + weightValue.data().getFloat());
System.out.println(“Bias is “ + biasValue.data().getFloat());
}
```
总结一下使用TensorFlow的大概流程
- 要获取训练数据(可以通过爬虫或者自己整理,越大量越好);
- 自己设计一个模型,也就是计算函数 (线性回归函数可以适应好多基于序列的数据,比如基于时间的数据预测);
- 输入的的参数定义为Placeholder;
- 将权值这些函数中需要在训练中调整的参数定义为Variable;
- 基于TensorFlow.math来写出模型的代码实现;
- 基于Tenso.math 实现损失函数;
- 选择优化器,并且用定义好的损失函数初始化;
- 构建session;
- 用训练数据训练模式:session.run。
那么你想用TensorFlow做什么呢?比如构建一个预测房价的模型、一个股票价格预测模式、一个彩票号码预测模型?