现在基本的概念已经介绍得差不多了,是时候实战演示一下如何利用神经网络来处理分类问题的。按照一直的做法,自己出题自己解决。这次拿一个现实生活中的一个小问题来练练手:根据人的身高,体重,把人划分成轻,正常,偏胖,肥胖这四种情况。按照现在流行的观点认为,人正常的BMI指数范围在20-25之间,低于20就是过轻,高于25低于28是偏胖,高于28就是肥胖了。
按照前面介绍的方法,解决问题的第一步是建模和打标签。根据问题的描述,可以把训练样本定义成一个2维向量,其中第1维表示身高(以米为计算单位),第2维是体重(以千克为单位)。训练样本有了之后就是打标。有点编程经验的人一眼就可以看出来这里可以用机器打标。标签数据是一个4维布尔值向量,第1维表示过轻,第2维表示正常,第3维表示偏胖,第4维表示肥胖。
第二步是建立模型,对于分类问题而言,最好的是先根据问题的规模来确定结点的数量,从上面的问题描述中大概可以猜出至少需要3个结点,才能划分出四个分类。因此不妨就将隐层结点数定义成3或4。这里建议大家训练模型的时候,根据先简单后复杂的原则来调整网络。因为一开始就训练很复杂的网络,不仅不一定得到很好的结果,而且训练的时间太长,也不利于检验模型的质量。
第三步就是训练模型,不停改进模型,直至得到较满意的精度。
下面给出训练代码:
import tensorflow as tf
import numpy as np
#设置训练集大小
train_set_size=1000
#随机生成身高数据,以男性1.7m为平均身高,上下浮动0.2m
height=tf.Variable(tf.random.truncated_normal([train_set_size,1],mean=1.7,dtype=tf.double,stddev=0.1))
#随机生成体重数据,以体重60kg为平均值,上下浮动20kg
weight=tf.Variable(tf.random.truncated_normal([train_set_size,1],mean=60,dtype=tf.double,stddev=20))
#归并数据
x=tf.concat([height,weight],1)
# 计算BMI指数
def getBMI(x):
return x[1] / (x[0] ** 2)
# 根据BMI指数自动生成标签
def getLable(x):
label = np.zeros(shape=[train_set_size,4],dtype=float)
for i in range(len(x)):
bmi = getBMI(x[i])
if bmi < 20:
label[i][0] = 1
elif bmi >= 20 and bmi < 25:
label[i][1] = 1
elif bmi >= 25 and bmi < 28:
label[i][2] = 1
else:
label[i][3] = 1
return label
# 将标签数据转换成tensorflow框架能处理的张量
y_=tf.convert_to_tensor(getLable(x))
# 设计两层神经网络,第一层隐含层共4个结点,激活函数为RELU;第二层为输出层也包含4个结点,对应4个分类,激活函数为softmax
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(4, activation='relu',input_shape=[2]),
tf.keras.layers.Dense(4,activation='softmax')
])
# 定义优化器,用于优化学习率等超参数指标
optimizer = tf.keras.optimizers.Adadelta(lr=1.0, rho=0.95, epsilon=1e-06)
# 编译模型,loss='categorical_crossentropy'指定损失函数为交叉熵,Metrics标注网络评价指标accuracy为准确率
model.compile(loss='categorical_crossentropy',
optimizer=optimizer,
metrics=['accuracy'])
# 打印网络参数模型,仅用于自检
model.summary()
# 重复训练3000次
for i in range(3000):
# 输入样本x和标签y, batch_size=1,每输入1条数据,就运行反向传播算法,更新网络参数。
model.fit(x=x,y=y_,batch_size=1)
# 准备测试集
test_size=3
h_t=tf.Variable(tf.random.truncated_normal([test_size,1],mean=1.7,dtype=tf.double,stddev=0.1))
w_t=tf.Variable(tf.random.truncated_normal([test_size,1],mean=60,dtype=tf.double,stddev=20))
x_t=tf.concat([h_t,w_t],1)
# 打印测试集内容和模型预测的结果
print(x_t)
print(model.predict(x_t))
下面是训练结果:
1000/1000 [==============================] - 0s 455us/step - loss: 0.1065 - accuracy: 0.9580
1000/1000 [==============================] - 0s 449us/step - loss: 0.0986 - accuracy: 0.9550
1000/1000 [==============================] - 0s 447us/step - loss: 0.1108 - accuracy: 0.9560
1000/1000 [==============================] - 0s 449us/step - loss: 0.1104 - accuracy: 0.9570
1000/1000 [==============================] - 0s 452us/step - loss: 0.0977 - accuracy: 0.9570
tf.Tensor(
[[ 1.87404843 51.79656559]
[ 1.69247713 54.36136267]
[ 1.80369327 53.8165295 ]], shape=(3, 2), dtype=float64)
[[1.0000000e+00 7.5280788e-35 0.0000000e+00 0.0000000e+00]
[9.9999952e-01 5.1134185e-07 7.3078410e-31 0.0000000e+00]
[1.0000000e+00 3.5876334e-22 0.0000000e+00 0.0000000e+00]]
连续训练3000次以上,准确率稳定在95%以上,证明算法是有效可靠的。而且从预测方面的能力看,效果还是挺不错的。从结果来看,一切都挺有趣的。但这个模型没有什么实际意义。正如前文所言,如果可以用普通的逻辑判断语句就可以实现分类,那根本就没有神经网络什么事。神经网络的分类能力主要还是用于数据挖掘上面,在海量数据中挖掘出人们还没或者不可能发现潜在的联系。
对于一些普通的业务需求而言,用神经网络来分类真的是脱裤子放屁---多此一举吗?在我看来,并不一定!!!
首先,从宏观的角度来看,用神经网络进行分类,起码是一种思维定式上的转变。过去我们认为,只有给电脑制定特定的运行逻辑(这个过程就是所谓的编程),电脑才能按照我们的想法工作。有了神经网络后,这个定式就打破了。实际上我们并不需要给电脑这么多条条框框,制定那么多那么细的规则。只要我们告诉电脑什么是对的,什么是错的?电脑自己就会去学习?这时候电脑是不是越来越有点人类的味道?没错,很多时候,我们作为成人,作为家长,对于小孩的教育,往往都是先从告诉他什么是对的,什么是错的开始。如果我们把一大段复杂冗长的逻辑,压缩成足够多的事例,是不是就等于把逻辑内嵌到神经网络的模型参数里面呢?上面的例子,就是一个很好的示范案例。它把求BMI,然后根据BMI指数来划分人的体重情况这一大段逻辑,压缩成了 (2 * 4 + 4)+ (4 * 4 + 4)=32 个参数,按每个参数以64位浮点型来存储,一共才浪费了32 * (64 / 8)= 256个字节。比原来的代码(净代码不含注释约占354个字节)节省了98个字节。
压缩代码容量好像没有什么了不起的。而且还以牺牲准确率为代价(准确率下降了4%)。神经网络看起来好像还是个废物点心呀!但请记住,“神经网络在某些分类问题上没用”这句话,是针对有编程基础的人而言。而且就算是有足够编程基础的大神,也不能保证所写的代码完全没有bug。一旦代码有bug,准确率就是一个未知之数了。所以再牛X的大牛,也还要写单元测试,要借助测试用例来检验自己的代码有没有bug。但问题是,有时候,大牛自己写的单元测试和生成的测试用例也是可能有bug的。。。。。从上面的分析可以看出,bug这种东西从人们学会编程的那一刻就已经是根深蒂固的存在了。那用神经网络训练的逻辑就没Bug了吗?有,肯定有。但就算有,也有解决办法让bug的出现概率限制在特定的误差范围内。
其次,神经网络将人工智能辅助编程变成一种可能。前面已经介绍过,一个多分支逻辑判断语句可以等价为一个n分类神经网络。其中一个条件分支就对应着一个分类。因此,可以将用一般的高级程序语言写的逻辑判断语句,直接转化成一个(或一组)神经网络。众所周知,神经网络有较好的鲁棒性(Robust),它仅与问题规模有关,而与问题所涉及具体的业务场景无关。因此用神经网络构建人工智能编程环境,能适应所有现阶段已知的业务类型。之前的示例很好的说明了这一点,不管我们是建立体重的BMI测量模型,还是建立工资收入与消费支出的统计学模型,神经网络都能轻松驾驭。
第三,人工智能辅助编程将极大地提升软件有效生产率。估计看到这里,不少码农朋友可能又要听到“狼来了”的声音。如果计算机自己都会编程了,那码农们是不是要集体失业了。不!正好相反,神经网络只是”辅助”人类编程,而不是去抢夺我们的主动权。首先,计算机没有自主意识,它不知道在什么时候干什么才是有意义的。其次,计算机能控制的范围都尽在人类的掌控之中。现今的所有计算机理论都是建立在严密的数学理论上面,也就是说,所有未来可能出现的情况,都可以在那一堆复杂的数学公式中找到答案。
人工智能辅助编程其实是广大码农的福音!自此之后,大家不用再花时间去996,写那枯燥乏味的业务代码。人们只需要按照业务需求场景,搭建好网络模型,然后把业务数据准备好,输入到神经网络中,最后就可以静待神经网络返回正确的结果。相信不久的将来,码农们已经可以摆脱代码的束缚,不再仅将关注点放在如何实现这件事上面,而更多的会从如何做好这件事去思考(这已经是产品思维了)
最后,人工智能辅助编程将进一步降低人们学习计算机编程的门槛。以后所有现在所谓的高级程序语言(如Java,C++,Ruby)可能都将成为历史。未来的编程语言随着NLP (自然语言理解)和人工智能辅助编程的持续发展,将会逐步演化为一门更贴近人类母语语法,更简练直观的语言。将来编程将不再是一门只有少数人掌握的技能,而是一项像喝水吃饭一样简单的本能。