因为某些原因需要字符识别发现有必要学习一下这个东西,准确率还是很不错的(扒的SciSharp-Stack-Examples-master里面的范例)。之后又做了点有趣的玩具,略有心得,或者说是这块知识学习的一个笔记。多年下来建立工程的时候还总是顺手就点VB.NET,所以全部代码都是VB.NET编写,有部分代码语法和C#也有较大差异,更不用说Tensorflow.net所谓的一致了(丫的个程序集组织和原版有很大差异,很多函数需要搜索对象浏览器,甚至有的名字根本不一样)。如有错误认知或不当描述还请不吝指正。
一、工程环境
1、.NET CORE:5.0(6.0)
2、NUGET:TensorFlow.NET 0.70.1、SciSharp.Models.TimeSeries 0.1.0
二、有啥东西
其实没啥东西。
1、序贯模型
2、函数模型(调用式)
3、模型的编译
4、模型的训练
5、模型的保存、加载(modle.save,modles.load)
三、亿点点注意
1、控制台工程,否则输出反馈很麻烦
2、输入、输出都是线性展开的
3、我完全不知道怎么在极小的数据集条件下让它准一些
尝试一:是否分类
Imports Tensorflow
Imports Tensorflow.KerasApi
Imports Tensorflow.NumPy
Public Class t1
Sub New()
'不知道配置环境启用立即执行(eager execution)是啥意思,范例里写了咱也写上。
Tensorflow.Binding.tf.enable_eager_execution()
'仅定义类型不可以,必须明确指明TF_FLOAT,并且用TF_INT32后面会出问题
Dim x = np.array(New Single(,) {{0, 0}, {0, 1}, {1, 0}, {1, 1}}, TF_DataType.TF_FLOAT)
Dim y = np.array(New Single(,) {{0}, {1}, {1}, {0}}, TF_DataType.TF_FLOAT)
Console.WriteLine(x.ToString)
Console.WriteLine(y.ToString)
'序贯模型,意思就是说整不了太复杂的,只能一根筋到底那种
Dim model = keras.Sequential
'一层一层往里填就行了
model.add(keras.Input(2)) '输入层,按说直接指定隐藏层的输入形状也可以
model.add(keras.layers.Dense(64, keras.activations.Relu)) '隐藏层,relu整流线型单元
model.add(keras.layers.Dense(1, keras.activations.Sigmoid)) '输出层,sigmoid(二分类)
'定义好了要编译一下子,指定学习参数,评价函数,反馈的啥啥啥(不写会出现一个未命名错误)
model.compile(keras.optimizers.Adam(), keras.losses.MeanSquaredError(), New String() {"accuracy"})
'在控制台打印模型结构
model.summary()
'训练模型,输入是x,正确输出是y,分1波(全部时间点只分一个序列),训练100次
model.fit(x, y, -1, 1000)
'测试一下,这个XOR只能用自己测试呗
Console.Write(model.predict(x))
End Sub
End Class
效果很不咋地。也许改成多层感知会好一些?记得当年写人工神经网络的基础代码时测试过类似的东西,准得很啊。
尝试二:就硬时间序列预测
Imports Tensorflow
Imports Tensorflow.KerasApi
Imports Tensorflow.NumPy
Public Class t2
Sub New()
Tensorflow.Binding.tf.enable_eager_execution()
Dim x = np.array(New Single(,) {{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6},
{4, 5, 6, 7},
{5, 6, 7, 8},
{6, 7, 8, 9},
{7, 8, 9, 10},
{8, 9, 10, 11},
{9, 10, 11, 12}}, TF_DataType.TF_FLOAT)
Dim y = np.array(New Single(,) {{5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}}, TF_DataType.TF_FLOAT)
Console.WriteLine(x.ToString)
Console.WriteLine(y.ToString)
Dim model = keras.Sequential
'指定一下输入形状——每次输入3列信息
model.add(New Keras.Layers.Dense(New Tensorflow.Keras.ArgsDefinition.DenseArgs With {
.Units = 100,
.Activation = keras.activations.Relu,
.InputShape = (1, 4)}))
'这个是输出层,输出真实值不用分类器
model.add(keras.layers.Dense(1))
model.compile(keras.optimizers.Adam(), keras.losses.MeanSquaredError(), New String() {"accuracy"})
model.summary()
'也不是学的次数越多越好,过拟合就给学傻了
model.fit(x, y, -1, 1000)
'预测一下,结果一点也不6
Dim z = New NumPy.NDArray(New Single(,) {{10, 11, 12, 13}})
Console.Write(model.predict(z))
End Sub
End Class
这个例子使用了另一种方法声明层,也是可以的,这种方式更接近于原版的写法。
尝试三:LSTM时间序列预测
Imports Tensorflow
Imports Tensorflow.KerasApi
Imports Tensorflow.NumPy
Public Class t3
Sub New()
Tensorflow.Binding.tf.enable_eager_execution()
Dim x = np.array(New Single(,) {{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6},
{4, 5, 6, 7},
{5, 6, 7, 8},
{6, 7, 8, 9},
{7, 8, 9, 10},
{8, 9, 10, 11},
{9, 10, 11, 12}}, TF_DataType.TF_FLOAT)
Dim y = np.array(New Single(,) {{5}, {6}, {7}, {8}, {9}, {10}, {11}, {12}, {13}}, TF_DataType.TF_FLOAT)
Console.WriteLine(x.ToString)
Console.WriteLine(y.ToString)
Dim model = keras.Sequential
Dim input = keras.layers.Input(4)
Dim lstm = New Keras.Layers.LSTM(New Tensorflow.Keras.ArgsDefinition.LSTMArgs With {
.Units = 100,
.Activation = keras.activations.Relu,
.InputShape = 4,
.ReturnSequences = True})
Dim dense1 = lstm.Apply(input)
Dim output = keras.layers.Dense(1).Apply(dense1)
model.add(output)
model.compile(keras.optimizers.Adam(), keras.losses.MeanSquaredError(), New String() {"accuracy"})
model.summary()
model.fit(x, y, -1, 1000)
Dim z = New NumPy.NDArray(New Single(,) {{10, 11, 12, 13}})
'有那么点意思了
Console.Write(model.predict(z))
End Sub
End Class
这个lstm,不知是原版就不支持还是tensorflow.net给阉割了,尝试了多输入每个进行lstm、直接做输出等都不行。这里看着好像是序贯式,实际上是使用函数方式形成了一个函数式的方式形成了一个复杂的层,而后把最后一个输出放在模型里,这样它的调用就成为整个模型的输入,输出就是整个模型的输出。
尝试四:多输出、多层SLTM时间序列预测
Imports Tensorflow
Imports Tensorflow.KerasApi
Imports Tensorflow.NumPy
Public Class t4
Sub New()
Tensorflow.Binding.tf.enable_eager_execution()
Dim x = np.array(New Single(,) {{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6},
{4, 5, 6, 7},
{5, 6, 7, 8},
{6, 7, 8, 9},
{7, 8, 9, 10},
{8, 9, 10, 11},
{9, 10, 11, 12}}, TF_DataType.TF_FLOAT)
Dim y = np.array(New Single(,) {{5, 6}, {6, 7}, {7, 8}, {8, 9}, {9, 10}, {10, 11}, {11, 12}, {12, 13}, {13, 14}}, TF_DataType.TF_FLOAT)
Console.WriteLine(x.ToString)
Console.WriteLine(y.ToString)
Dim model = keras.Sequential
Dim input = keras.layers.Input(4)
Dim lstm1 = New Keras.Layers.LSTM(New Tensorflow.Keras.ArgsDefinition.LSTMArgs With {
.Units = 128,
.Activation = keras.activations.Relu,
.InputShape = 4})
Dim dense1 = lstm1.Apply(input)
'对称起来
Dim lstm2 = New Keras.Layers.LSTM(New Tensorflow.Keras.ArgsDefinition.LSTMArgs With {
.Units = 128,
.Activation = keras.activations.Relu,
.InputShape = 4,
.ReturnSequences = True})
Dim dense2 = lstm2.Apply(dense1)
Dim output2 = keras.layers.Dense(2).Apply(dense2)
model.add(output2)
model.compile(keras.optimizers.Adam(), keras.losses.MeanSquaredError(), New String() {"accuracy"})
model.summary()
model.fit(x, y, -1, 1000)
Dim z = New NumPy.NDArray(New Single(,) {{10, 11, 12, 13}})
'有那么点意思了
Console.Write(model.predict(z))
End Sub
End Class
就随便写几个参数嘛,没看出来准确率有多大进步。主要是从一个元素输出变成多个元素了。可能你也发现多次训练的时候每次结果都不一样,原因有两个:
1、Tensorflow.Binding.tf.set_random_seed()未设置,设置之后随机种子固定。
2、model.fit的shuffle参数设置为不舒服。。。。咳咳,设置为flase,不再次打乱顺序。
另外,代码中也有几个提示:
1、每行数据(时间点上的特征数)越丰富,越容易训练,用把标量转换为矢量、给周期性数据串一个并行的周期波(参考官方温度预测的代码方式,记得应该是按行串了每天和每年的正弦值,使模型能够按照这个进行载波)等方法提供更准确的数据描述效果就更好。
2、整个数据集划分的批次多少对训练结果的准确率有很大影响。(上面的代码均为-1,为1批)
尝试五:将输出“二值化”,下面的尝试是独热编码(One-Hot,就是说输出数组一行只有一个1)
Imports Tensorflow
Imports Tensorflow.KerasApi
Imports Tensorflow.NumPy
Public Class t5
Sub New()
Tensorflow.Binding.tf.enable_eager_execution()
Dim x = np.array(New Single(,) {{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6},
{4, 5, 6, 7},
{5, 6, 7, 8},
{6, 7, 8, 9},
{7, 8, 9, 10},
{8, 9, 10, 11},
{9, 10, 11, 12}}, TF_DataType.TF_FLOAT)
Dim y = np.array(New Single(,) {{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}}, TF_DataType.TF_FLOAT)
Console.WriteLine(x.ToString)
Console.WriteLine(y.ToString)
Dim model = keras.Sequential
Dim input = keras.layers.Input(4)
Dim lstm1 = New Keras.Layers.LSTM(New Tensorflow.Keras.ArgsDefinition.LSTMArgs With {
.Units = 128,
.Activation = keras.activations.Relu}).Apply(input)
Dim lstm2 = New Keras.Layers.LSTM(New Tensorflow.Keras.ArgsDefinition.LSTMArgs With {
.Units = 64,
.Activation = keras.activations.Relu,
.ReturnState = True}).Apply(lstm1)
Dim output1 = keras.layers.Dense(15, keras.activations.Softmax).Apply(lstm2)
model.add(output1)
'损失函数CategoricalCrossentropy和Softmax对应
model.compile(keras.optimizers.Adam(0.01), keras.losses.CategoricalCrossentropy, New String() {"accuracy"})
model.summary()
model.fit(x, y, -1, 1000)
Dim z = New NumPy.NDArray(New Single(,) {{10, 11, 12, 13}})
'有那么点意思了
Console.Write(model.predict(z))
End Sub
End Class
注意激活函数和最后一层的对应关系。官方文档对不同激活函数都有描述,但恕我不知怎么用,所以就自行脑补了一些:
0、不用激活函数,那输出的值应该是不做处理,换句话说是更接近真实输出才对。
1、relu层,顾名思义吧,将数据整流。不会言传只好意会。
2、softmax层,把数据和真实值的差距映射到[0,1]区间。可以处理一组数据,就给人赶脚比Sigmoid强大一些。
尝试六:把输出里面的1整多点
这个是最后我的实际应用,不贴代码只说一下整个处理过程。
1、原始数据才几千行,每行几个特征,和人家十年每10分钟十好几个特征的比不了。所以通过脑补把数据的其他特征丰富一下,成功从几个变成上百。
2、从1到n每sequence_length(每批数据的行数)做一批,然后按sequence_length划分数据集,就像上面代码所作的那样,把1、2、3、4、5、6……变成1、2、3、4;2、3、4、5;3、4、5、6……这样(实际效果还是很勉强的,因为数据应用次数是一个明显的纺锤形,中部用的多两端少,可我的数据没有什么周期性,打算一会尝试sequence_length/2重叠的方式,把中间的大肚子减减看看是不是有进步)。
3、把输出[1,x],[1,y],[1,z]映射到[1,x+y+z],而后编码,所以每行输出有3个1。得到输出之后自己解码就可以了。
4、构建一个多输入的网络,让每组数据数据经过自己的多层感知(relu)之后串起来进入lstm层,最后softmax。
总之用了几天时间撸了一下,发现这玩意,博大故而精深啊!可能应该多去逛逛官方社区和博客才能学到真正的应用,至少怎么设计模型度娘从没告诉我。也不知这种找点啥啥没有的情况要持续十几年之后还要持续多久,反正对我这种业余选手影响还是很大的。为啥影响很大?去抠鸟语文档那不破坏了业余选手的形象嘛!