Keras 分类问题探究

序言

Keras是一个机器学习框架,提供了一系列用于训练、评估、测试、预测的api,可以自由的构建机器学习模型。这里通过一些具体的分类问题来探究模型各个参数和结构对模型效果的影响。

神经网络实现异或逻辑

异或逻辑是在神经网络中比较重要的一种逻辑。神经网络诞生之初,只有单层神经网络,出色的解决了与、或、非逻辑,但是却无法解决异或的问题。后来人们发现多层神经网络可以解决异或问题,于是神经网络的应用得到进一步推广。

逻辑构建神经网络

我们先看一下单个神经元是如何实现与、或非逻辑的。

  • And
0.3
0.3
x1
Out
y
x2

O u t = f ( w 1 x 1 + w 2 ∗ x 2 ) Out=f(w_1x_1+w_2*x_2) Out=f(w1x1+w2x2),其中 w 1 = 0.3 , w 2 = 0.3 w_1=0.3,w_2=0.3 w1=0.3,w2=0.3,取激活函数f为
f ( x ) = { 0 if x <0.5 1 if x ≥ 0.5 f(x)=\begin{cases} 0 & \text {if x <0.5} \\ 1 & \text {if x ≥ 0.5} \end{cases} f(x)={01if x <0.5if x ≥ 0.5,实际训练的时候,可以选取sigmoid函数配合阈值达到同样效果。

  • Or
0.6
0.6
x1
Out
y
x2

其中 w 1 = 0.6 , w 2 = 0.6 w_1=0.6,w_2=0.6 w1=0.6,w2=0.6,激活函数仍然选取And的激活函数。可以看到,对于And和Or逻辑,神经元结构和激活函数没有任何区别,只是神经元的权重有所区别

  • Not
-1
1
x
Out
bias

O u t = w x + b Out=wx+b Out=wx+b,其中 w = − 1 , b = 1 w=-1,b=1 w=1,b=1,Not逻辑和And Or的一个重要区别是只有一个输入,同时,需要一个bias来辅助运算,从非操作就可以看出神经网络结构中bias偏置的重要性。
接下来看看XOR逻辑如何构建。使用单个或者单层神经网络或者神经元能不能实现异逻辑呢?答案是不能,这里不从理论上给出证明,只给一个直观解释,后面会给另外一个直观解释。对于单个神经元,对于其中一个输入的权重是固定的,正向为增强,负向为削弱,但是异或要求[0,0]和[1,1]输出都为0,对于单层神经元,对于每一个输入,增强也不行,削弱也不行。实际上,我们看看三种操作的卡诺图可以指导,与或非都是线性可分的,异或在二维上不是线性可分的(与或的分界线是 f ( x ) = w 1 x 1 + w 2 y 2 + b f(x)=w_1x_1+w_2y_2+b f(x)=w1x1+w2y2+b,与或的分界线,b的取值不同)。SVM是通过核函数把低维向量映射到高维空间从而达到可分,神经网络则是通过多层神经元来达到可分的目的。

and01
000
101
or01
001
111
not01
-10
xor01
001
110
  • 构建XOR的神经网络
    根据XOR卡诺图 y = ( x 1 & & ! x 2 ) ∣ ∣ ( ! x 1 & & x 2 ) y=(x_1 \&\& !x_2) || (!x_1 \&\&x_2) y=(x1&&!x2)(!x1&&x2),可以看到XOR实际上是两步,第一步是求或两侧的两个元素的值,第二步是对第一步中的值求或操作,不难得到神经网络结构和权重
0.3
0.6
-0.3
0.6
-0.3
0.3
0.3
0.3
x1
H1
Out
y
H2
x2
bias

H 1 = x 1 & & ! x 2 = x 1 & & ( 1 − x 2 ) = f ( w 1 x 1 + w 2 ( 1 − x 2 ) ) \begin{aligned}H_1 &=x_1\&\&!x_2\\ &=x_1\&\&(1-x_2) \\ &=f(w_1x_1+w2(1-x_2)) \end{aligned} H1=x1&&!x2=x1&&(1x2)=f(w1x1+w2(1x2))
同理
H 2 = f ( w 1 x 2 + w 2 ( 1 − x 1 ) ) \begin{aligned}H_2 =f(w_1x_2+w2(1-x_1)) \end{aligned} H2=f(w1x2+w2(1x1))
y = H 1 ∣ ∣ H 2 y=H_1||H_2 y=H1H2
当然上述神经网络是我们通过理论构建出来的,使用的激活函数实际不可导,这样就很难使用梯度下降法进行训练,实际上,这里可以使用sigmoid函数取代上述激活函数,但是权重也要做相应调整。

使用神经网络进行训练

def gen_data(fun, num):
    data = [[np.random.randint(0, 2), np.random.randint(0, 2)] for i in range(num)]
    label = list(map(fun, data))
    onehot = keras.utils.np_utils.to_categorical(label, 2)
    return np.array(data), np.array(onehot)


def test_and():
    data, onehot = gen_data(lambda e: e[0] ^ e[1], 300)
    inputs = Input(shape=(2,))
    activation = Activation('relu')
    x = Dense(2, activation=activation)(inputs)
    y = Dense(2, activation='softmax')(x)
    model = Model(inputs=inputs, outputs=y)
    model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['acc'])
    model.fit(data, onehot, batch_size=5, epochs=20)
    model.evaluate(data, onehot)

构建神经网络,即使迭代2000轮,但是发现正确率只有80%左右,很难收敛到100%的准确率,但是And/Or则可以很快收敛。这个有点奇怪,看上面我们使用卡诺图推导的模型,感觉简单调下参数,就能达到100%的正确率,为什么模型最后不能收敛到100%正确的参数呢?我验证了下调整的参数,在正确的情况下,得到的logit向量,正确的只比错误的高一点点,这样的交叉熵肯定是不够小的。例如,对于异或结果为0的,输出为 [ 0.55 , 0.45 ] [0.55,0.45] [0.55,0.45],对于异或结果为1的,输出为 [ 0.45 , 0.55 ] [0.45,0.55] [0.45,0.55],这样对于100%正确率的情况,交叉熵为0.5978,但是,如果输出的logit是 [ [ 0.9993 , 0.0007 ] , [ 0.3144 , 0.6856 ] , [ 0.3144 , 0.6856 ] , [ 0.3144 , 0.6856 ] ] [[0.9993, 0.0007], [0.3144, 0.6856], [0.3144, 0.6856], [0.3144, 0.6856]] [[0.9993,0.0007],[0.3144,0.6856],[0.3144,0.6856],[0.3144,0.6856]],交叉熵为0.47,虽然对于输入为 [ 1 , 1 ] [1,1] [1,1]的情况,交叉熵会比较大,但是这个解的总平均交叉熵是更小的。因为异或在二维上不是线性可分的,使用categorical_crossentropy作为损失函数的情况下,有一类分错的损失比全分对的损失还要小。

为了证明这个原因,更换损失函数为mse之后,模型可以在800次左右的迭代后收敛。对于And/OR也可以达到类似效果。具体的原因还得再研究研究。实际上,修改优化器的配置,也能达到收敛效果。
后来重新观察了下,当迭代到2000轮的时候,交叉熵还在不断减小,到大约10000轮的时候,模型分类正确率就可以达到100%了,还是要观察指标,保持耐心。当然,收敛慢的问题,可以通过优化优化器(optimizer参数)来实现。
loss和accuracy随着训练轮数的变化

输入也使用onehot

因为输入是两个维度,每个维度实际都是使用各自真实的值作为label,实际上可以将输入也转化成onehot来表示。即
[ 0 , 0 ] − > [ 0 , 1 , 0 , 1 ] [ 0 , 1 ] − > [ 0 , 1 , 1 , 0 ] [ 1 , 0 ] − > [ 0 , 1 , 1 , 0 ] [ 1 , 1 ] − > [ 0 , 1 , 0 , 1 ] [0,0]->[0,1,0,1]\\ [0,1]->[0,1,1,0]\\ [1,0]->[0,1,1,0]\\ [1,1]->[0,1,0,1] [0,0]>[0,1,0,1][0,1]>[0,1,1,0][1,0]>[0,1,1,0][1,1]>[0,1,0,1]

def input_to_onehot(data):
    return np.array(list(map(lambda e: [e[0], 1 - e[0], e[1], 1 - e[1]], data)))

实验证明这个做法对效果并没有什么改善。这个也很好理解,因为本来就是0,1表示,拓展维度并没有帮助。

手动做特征交叉

输入是 [ x 1 , x 2 ] [x_1,x_2] [x1,x2],我们可以对输入特征进行扩展,比如
[ x 1 , x 2 ] − > [ x 1 , x 2 , x 1 & x 2 ] [x_1,x_2]->[x_1,x2,x_1\&x_2] [x1,x2]>[x1,x2,x1&x2],也就是增加一维手动特征交叉的维度,这一维可以是两者的与或者或操作,实验证明仍旧采用CategoricalCrossentropy作为损失函数,也能达到较好的效果。实际上,不一定是因为增加了一个交叉特征,而更可能是将隐藏层改成了3,导致模型参数更多,表达能力更强了。在第2000轮的时候,正确率就已经达到100%了。

def extend_input(data):
    return np.array(list(map(lambda e: [e[0], e[1], e[0] & e[1]], data)))

在这里插入图片描述

集成学习

在前文中,我们使用白盒方法(卡诺图)构建了模型结构,对于模型,我们也可以通过分别训练的方式构建一个模型,也就是把模型分成三个部分:

  1. h 1 = x 1 & ! x 2 h_1=x_1\&!x_2 h1=x1&!x2
  2. h 2 = ! x 1 & x 2 h_2=!x_1\&x_2 h2=!x1&x2
  3. y = h 1 ∣ h 2 y=h_1|h_2 y=h1h2

自动调超参

keras是支持超参的调参的,可以试用下效果。

决策树方法

为什么我的模型这么愚蠢,学习这么慢?

  1. 调整优化器的学习率,默认是0.001,学习就太慢了;
  2. 引入辅助目标或者手动做交叉特征。比如在异或问题上,可以加入辅助目标,预测 x 1 x_1 x1是否为1,可以得到更快的收敛速度。异或确实是一个很好的问题,很容易陷入局部最优。
  3. 使用更好的优化器,比如rmsprop

统计输入向量中1的个数

数据生成

不同的问题需要使用不同的数据预处理方法、采样策略、模型结构、优化器、分批训练方法来进行训练和预测。为了能够快速构建数据,方便检验结果,这里以一个具体的数学问题来作为数据:统计一个输入向量中 1 1 1的个数,并且把这个问题当做多分类问题来处理。例如对于 [ 0 , 1 , 0 , 1 , 0 , 0 , 0 , 1 ] [0,1,0,1,0,0,0,1] [0,1,0,1,0,0,0,1]中有3个 1 1 1,故输出应该为 [ 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 ] [0,0,0,1,0,0,0,0] [0,0,0,1,0,0,0,0](3的onehot编码),这个问题如果当做白盒来处理,相当简单,可以计数 1 1 1的个数或者取加和即可,但是我们可以通过模型来解决这个问题来研究模型的一些性质。可以使用下面的方法来生成训练、验证、测试、预测数据。输入参数dim

def gen_data_and_labels(dim, num) -> ([[int]], [[int]], [int]):
	'''
	dim 生成的输入向量的维数
	num 生成的数据的个数
	'''
    data = np.array([[1 if np.random.random() > 0.5 else 0 for i in range(dim)] for j in range(num)])
    label = list(map(lambda arr: reduce(lambda a, b: a + b, arr, 0), data))
    onehot = np.array(list(map(lambda l: to_categorical(l, dim + 1), label)))
    return data, onehot, label

单次训练

接近真实

实际问题中,我们的数据不可能这么完善,可以通过添加噪声和屏蔽特征来模拟实际情况。添加噪声模拟的是个别instance偏差过大或者采样过程中数据有误;屏蔽特征模拟的是我们不能获取到相关问题的所有信息,对模型来说就相当于屏蔽特征。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值