Tensorflow异或问题及可视化

引入

这次我们来看看机器学习中一个比较有趣的问题,异或问题。顺便看一看神经网络的非线性效果
大概是在感知机模型被提出后,有dalao发现,这个东西不能处理异或问题

异或问题: 异或(XOR)问题可以看做是单位正方形的四个角,响应的输入模式为(0,0),(0,1),(1,1),(1,0)。第一个和第三个模式属于类0,即 0 XOR 0 = 0, 1 XOR 1 = 0;另一方面,第二个和第四个属于类1,即 1 XOR 0 = 1,0 XOR 1 = 1

如图:
在这里插入图片描述
我们可以发现,没办法找出一条直线,使其正好将两种颜色的分隔开来,这就是感知机不能解决异或问题的根本原因,事实上,所有的线性分类模型都无法处理异或分类问题

而神经网络中加入了激活函数,比如最常用的ReLU函数,或者很多人最开始接触的sigmoid函数等等。

神经网络(全连接)的层结构一般为 L i = a ( W i L i − 1 + b i ) L_i = a(W_iL_{i-1} + b_i) Li=a(WiLi1+bi)a(x)为激活函数,可以看到其内部为一个线性运算,如果没有激活函数,或者说 a ( x ) = x a(x) = x a(x)=x 因为线性的缘故,那么无论这个网络叠多少层,到头来还是和单层的线性网络没什么区别。

激活函数为神经网络加入了非线性,因此神经网络可以解决线性模型不能解决的非线性问题。


实践

说了这么多,我们看看实际的效果如何:这里我们先对神经网络的训练数据做一下准备,

x_data = [[1, 1], [1, 0], [0, 1], [0, 0]]
y_data = [[0], [1], [1], [0]]

这个问题,可以说是一个回归问题,也可以说是一个分类问题,这里我就当回归处理了。
顺便补一下需要导入的东西:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

然后创建两个占位符,分别作为输入和正确回归

x = tf.placeholder ("float", [None, 2])
y_t = tf.placeholder ("float", [None, 1])

然后就是创建权重啦,为了方便可视化,这里的网络结构为2[输入] × 2[隐藏] × 1[输出]
初始化使用截断正态分布随机,是很常用的初始化方法,对于神经网络来说,权重的初始化很重要,关系到训练的快慢,以及能否到达一个较好的局部最优点。偏置就初始为0

w1 = tf.Variable(tf.truncated_normal([2, 2], stddev=1))
b1 = tf.Variable(tf.zeros(2))

w2 = tf.Variable(tf.truncated_normal([2, 1], stddev=1))
b2 = tf.Variable(tf.zeros(1))

接下来就是连接不同的层,并穿插使用一些激活函数,loss函数就用常用的MSE(均方误差),优化器使用Adam(快就完事了)。。
本篇的话,sigmoid和relu都会可视化一下

a1 = tf.matmul (x, w1) + b1
l1 = tf.nn.sigmoid (a1)
# l1 = tf.nn.relu (a1)

a2 = tf.matmul (l1, w2) + b2
y  = tf.nn.sigmoid (a2)
# y = tf.nn.relu (a2)

loss = tf.reduce_mean (tf.square (y_t - y))

train_step = tf.train.AdamOptimizer().minimize(loss)

然后就是普普通通的训练咯,别忘记先初始化变量:

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	for i in range(5000):
		loss_val, _ = sess.run([loss, train_step], feed_dict={x: x_data,y_t: y_data})
		fig = 0
		if i % 500 == 0:
			print('{}:loss={}'.format(i,loss_val))

激活函数使用sigmoid时,i = 3000左右就能(应该更早)就能很好的识别两类了,以下为输出:
[[0.09495745]---->0
[0.88782966]----->1
[0.8935238 ]------>1
[0.11747289]]----->0
我们来看看神经网络到底做了什么:
在这里插入图片描述
这里用matplotlib画了4个子图,第一个是原始数据。第二个(横着数)是a1,也就是只经过输入层到隐藏层的线性变化,可以看到,原始数据经过线性变换,进行了旋转,平移(这个要看坐标轴,plot默认居中),还有切变和缩放,不过,这样并不能改变原数据的非线性性质,也就是四个点还是非线性可分的

第三个图就是由子图2经过一次sigmoid变换后的新空间,很明显,弯曲,而且随着训练的进行,绿点被拉到了图中接近两个对角线的地方,而红点则像是被对折一样,来到了一起,这样我们就能找一个超平面,将两类点分割,如图中的红线,红线其实就是根据w2和b2来画的(x*w2[0] + y * w2[1] + b2 = 0这条直线)

所以,优化到最后,这条直线应该正好处于红点绿点的最中间,并且与绿点的连线平行,此时有loss最小。

失败的情形

还记得之前说过,权重的初始化很重要,在实验的过程中,本人还发现两种不能优化的情况,原因就是初始化的参数不是“很好”的参数,当然,根本原因我更倾向于网络太小了,要是改成2 × 4 × 2我觉得都不会出现以下的奇葩情况……

情况1:

在这里插入图片描述

情况2:

在这里插入图片描述
这两种情况,应该就是卡在了某些很烂的局部极小值、、所以,如果你的网络表现不好,不要着急否决,多实验几次,说不定就是参数没选对(滑稽

然后我们再来看看ReLU激活函数的表现吧:
在这里插入图片描述
首先,激活函数的形式决定了可视化的样子。sigmoid是把整个空间映射到 [0,1] * [0,1] 的方形内,而ReLU看上去像是一个只有第一象限的坐标系(因为ReLu的稀疏激活性,把小于0的全忽略了)

还有一点就是,sigmoid中的超平面(红线)是在红点和绿点之间,平行于绿点连线的,而ReLU的是过红点,平行于绿点连线。这是由于这个超平面是做回归用的,并且之后还会被激活一道,我们知道,sigmoid的值域在[0. 1]之间,并且图像关于(0, 0.5) 中心对称:(跑到百度拿了张图)
在这里插入图片描述
如果一个点不在直线上,那么我们把这个点带到直线方程中,会得到一个非零值,更一般的说,这个点在直线的不同侧,得到的值的正负性不一样。之后,这个值会带入sigmoid,所以,红点要为0,绿点要为1,我们需要激活前,红点带入直线的值为负值,且越小越好;绿点为正值,且越大越好。为了同时满足这两个要求,所以我们的超平面只能尽量保持到红点和绿点的距离一样。

而对于ReLU,因为其大于0的部分是线性的,我们要求红点为0,只需要让红点正好在超平面上(红线上),则带入方程就为0,而绿点,只需要带入方程大于0,且尽量等于1即可,因此直线会过红点,且尽量与绿点连线平行。(再去拿张ReLU的图片)
在这里插入图片描述

完整代码

由于可视化部分的代码较长……所以最后再放:
还有就是matplotlib的互交式画图,还有内容的清空emm一直找不到有效的方式……所以只能用笨办法咯,,,

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

x_data = [[1, 1], [1, 0], [0, 1], [0, 0]]
y_data = [[0], [1], [1], [0]]

x = tf.placeholder ("float", [None, 2])
y_t = tf.placeholder ("float", [None, 1])

w1 = tf.Variable(tf.truncated_normal([2, 2], stddev=1))
b1 = tf.Variable(tf.zeros(2))

w2 = tf.Variable(tf.truncated_normal([2, 1], stddev=1))
b2 = tf.Variable(tf.zeros(1))

a1 = tf.matmul (x, w1) + b1
l1 = tf.nn.relu (a1)

a2 = tf.matmul (l1, w2) + b2
y  = tf.nn.relu (a2)

loss = tf.reduce_mean (tf.square (y_t - y))

train_step = tf.train.AdamOptimizer().minimize(loss)

d1 = [[0, 0], [1, 1]]
d2 = [[0, 1], [1, 0]]

with tf.Session() as sess:
	sess.run(tf.global_variables_initializer())
	
	
	for i in range(5000):
		loss_val, _ = sess.run([loss, train_step], feed_dict={x: x_data,y_t: y_data})
		fig = 0
		if i % 500 == 0:
			print('{}:loss={}'.format(i,loss_val))
			
			plt.close()
			plt.subplots(figsize=(10, 10))
			mngr = plt.get_current_fig_manager()  # 获取当前figure manager
			mngr.window.wm_geometry("+110+110")  # 调整窗口在屏幕上弹出的位置

			plt.subplot (221)

			dd1 = np.array(d1)
			dd2 = np.array(d2)
			plt.scatter(dd1.T[0], dd1.T[1], color='red')
			plt.scatter(dd2.T[0], dd2.T[1], color='green')
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t1, t2])
				p = np.array(p)
				ph = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)
			
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t2, t1])
				p = np.array(p)
				ps = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)

			plt.subplot (222)
			dd1 = np.array(sess.run (a1, feed_dict = {x: d1}))
			dd2 = np.array(sess.run (a1, feed_dict = {x: d2}))
			plt.scatter(dd1.T[0], dd1.T[1], color='red')
			plt.scatter(dd2.T[0], dd2.T[1], color='green')
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t1, t2])
				p = np.array(sess.run (a1, feed_dict = {x: p}))
				ph = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)
			
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t2, t1])
				p = np.array(sess.run (a1, feed_dict = {x: p}))
				ps = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)
			
			plt.subplot (223)
			
			dd1 = np.array(sess.run (l1, feed_dict = {x: d1}))
			dd2 = np.array(sess.run (l1, feed_dict = {x: d2}))
			plt.scatter(dd1.T[0], dd1.T[1], color='red')
			plt.scatter(dd2.T[0], dd2.T[1], color='green')
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t1, t2])
				p = np.array(sess.run (l1, feed_dict = {x: p}))
				ph = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)
			
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t2, t1])
				p = np.array(sess.run (l1, feed_dict = {x: p}))
				ps = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)

			plt.subplot (224)

			w2_ = sess.run (w2)
			b2_ = sess.run (b2)

			xx_ = [-1, 1]
			yy_ = [(xx_[0] * w2_[0] + b2_) / - w2_[1], (xx_[1] * w2_[0] + b2_) / - w2_[1]]
			
			dd1 = np.array(sess.run (l1, feed_dict = {x: d1}))
			dd2 = np.array(sess.run (l1, feed_dict = {x: d2}))
			plt.scatter(dd1.T[0], dd1.T[1], color='red')
			plt.scatter(dd2.T[0], dd2.T[1], color='green')
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t1, t2])
				p = np.array(sess.run (l1, feed_dict = {x: p}))
				ph = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)
			
			for i in range (20):
				t1 = (i - 10) / 5
				p = []
				for j in range (20):
					t2 = (j - 10) / 5
					p.append ([t2, t1])
				p = np.array(sess.run (l1, feed_dict = {x: p}))
				ps = plt.plot (p.T[0], p.T[1], color = "blue", alpha=0.5)

			ph = plt.plot (xx_, yy_, color = "red", alpha=0.5)

			plt.pause (1)
			plt.show (block = False)

	print (sess.run (y, feed_dict = {x: x_data}))
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值