序言
最近在看机器学习相关的知识,然而当时看懂了,时间一久就忘了。我可不是张无忌在学太极,忘了是不行的。记下一些当时看懂的,好的基本点和相关链接,尤其是当时的aha moment,希望后续后帮助。长远看当知识形成体系之后,会再做整理。
理解这些方法,可以参照各个博客,但是很多博客都只有只言片语,很多都语焉不详,但是看看原始论文,就能恍然大悟。
概述
机器学习要解决的问题可以分成两大类:分类和回归。所谓分类,就是顾名思义的分类;相对的,回归是预测具体的值而不是一个类别。举个例子,预测明天的天气(晴、阴、雨、雪、雾)是分类问题;预测明天的气温是回归问题,因为温度是连续的,不是离散的几个值。
从方法上来说,可以分成几大类:传统的统计机器学习(贝叶斯、马尔科夫等),决策树(xgboost, adaboost, gdbt等)和神经网络(DNN,CNN,RNN).
各个方法基本都涉及到的问题:损失函数,模型训练等。
特征工程
One-Hot 编码
这个在神经网络(不止限于神经网络是很重要的一个概念),一个简单的理解:
比如根据水果的特征来对水果进行分类,颜色是其中的一个特征。我们可以只用一个维度来表示这个特征,0-红色,1-黄色,2-绿色,3-橙色,4-紫色,5-白色。这里的问题是特征表示的是一个相似性的距离问题,因为只有一个数字,这里就默认了黄色比紫色和红色相比更加相似,然而这个假设是不一定成立的,即使成立,应该由训练来得出,而不应是处理特征的时候就加了这个假设。为了解决这个问题,常用的解决办法是One-Hot编码,对于红色、黄色、绿色的编码分别为
R e d = [ 1 , 0 , 0 , 0 , 0 , 0 ] Y e l l o w = [ 0 , 1 , 0 , 0 , 0 , 0 ] G r e e n = [ 0 , 0 , 1 , 0 , 0 , 0 ] . . . \begin{aligned} Red&=[1,0,0,0,0,0]\\ Yellow&=[0,1,0,0,0,0]\\ Green&=[0,0,1,0,0,0]\\ ... \end{aligned} RedYellowGreen...=[1,0,0,0,0,0]=[0,1,0,0,0,0]=[0,0,1,0,0,0]
除了对应的位置为1其余都是0,这样好处就是Red/Yellow/Green在空间的距离都是1,他们相互之间都是一样的,坏处也显而易见,特征维数爆炸。如果很多特征都用One-Hot来编码,总的特征维数就是所有可用label的乘积,维数相当高。维数高有两个问题:1.运算量大;2.数据稀疏。通常的做法是在输入之后加入一个维度低很多的神经网络隐层,做一个降维,这个降维之后的向量就是embedding(全连接层只是降维的一种手段,还有其他的,比如FM里的 w i = < v i , v j > w_i=\bold{<v_i,v_j>} wi=<vi,vj>)
这样表示并非总是合理,比如在NLP中,实际使用One-Hot编码不能体现出近义词和非近义词的差别,使用One-Hot就不太合理。在NLP中也是会使用多加一层神经网络计算embedding,使用embedding表示单词,可以解决近义词问题。并且由于NLP中,段落本身就是文本,可以只用非监督学习来得出embedding.
传统统计学方法
这部分积累还比较少,待之后补充
决策树
据说决策树的方法在各种kaggle比赛中大行其道,然而从原理上,这么个感觉上相对粗糙的做法很难想象能有这么好的效果。这种现象产生的原因可能是kaggle上的问题数据量相对较小,数据量较小的时候,决策树会把信息上最大的特征放在第一层分类,这是一种贪心算法,相当于在人的干预下对参数作了优化,这种优化在数据量较小的情况下,NN模型是很难学到的。但是当数据量变大的时候,这种“贪心”中的局部最优问题会有一些影响。
决策树的生成一些常用算法C4.5,ID3和CART等.
C4.5 https://www.cnblogs.com/gispathfinder/p/5770217.html
ID3 C4.5是ID3的提升
CART 损失函数是GINI系数
这几个算法都是一些数学公式和定义,顾名思义也挺好理解的。
一些决策树方法
神经网络
组件
Logit函数
l
o
g
i
t
(
p
)
=
l
n
(
p
1
−
p
)
=
w
T
x
logit(p)=ln(\frac p {1-p})=w^Tx
logit(p)=ln(1−pp)=wTx
实际上logit函数是sigmoid函数的反函数,一个神经网络最终的输出概率会在输出层的使用一个活化函数
Sigmoid函数
p
=
s
i
g
m
o
i
d
(
w
T
x
)
=
1
1
+
e
−
w
T
x
p=sigmoid(w^Tx)=\frac 1 {1+e^{-w^Tx}}
p=sigmoid(wTx)=1+e−wTx1
sigmoid函数有有很多很好的特性:
- 输出范围(0,1),恰好在概率区间中;
- 其导数为 f ( z ) ′ = f ( z ) − f ( z ) 2 f(z)'=f(z)-f(z)^2 f(z)′=f(z)−f(z)2,因为导数可以表示为自身的形式,在进行梯度下降中可以减少计算量。
Softmax
Softmax实际是对Sigmoid函数的推广,Sigmoid只适用于二元输出场景,对于多元,可以只用softmax
s
o
f
t
m
a
x
(
x
i
)
=
e
x
i
∑
j
=
1
n
e
x
i
softmax(x_i)=\frac {e^{x_i}} {\sum\limits_{j=1}^n{e^{x_i}}}
softmax(xi)=j=1∑nexiexi
例如如果输出是
l
o
g
i
t
(
x
)
=
[
−
0.1
,
2
,
4
]
logit(x)=[-0.1,2,4]
logit(x)=[−0.1,2,4],这样的输出是没法用作概率的,并且当最终的输出不加范围限制的时候,模型不太容易收敛。为了解决这个问题,可以对输出做一个softmax(可以使用python的scipy.special.softmax进行计算)变换得到
p
(
x
)
=
[
0.01554136
,
0.04668881
,
0.93776983
]
p(x)=[0.01554136,0.04668881,0.93776983]
p(x)=[0.01554136,0.04668881,0.93776983].
这里我们考虑一个思考题,已知softmax之后的概率,能否求出原始的logit呢?答案是不能,因为有无数多组解。可以写出三个式子,但是其中只有两个是线性无关的。
{ e x 1 ∑ i = 1 3 e x i = a e x 2 ∑ i = 1 3 e x i = b e x 3 ∑ i = 1 3 e x i = c \def\ss {\sum\limits_{i=1}^3e^{x_i}} \gdef\eqai#1{\frac{e^{x_#1}}{\ss}} \begin{cases} \eqai{1}=a\\ \eqai 2=b\\ \eqai{3}=c\\ \end{cases} ⎩⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎧i=1∑3exiex1=ai=1∑3exiex2=bi=1∑3exiex3=c
明显,方程两边同时1-(1)-(2)即可得到(3)。
如何理解softmax是sigmoid的更一般形式呢?对于sigmoid函数,输入是一个值 x x x,而不是向量,但是可以转换成向量的形式 [ x , 0 ] [x,0] [x,0],这样,使用softmax函数和sigmoid函数的结果就是一样的。
Hierachical Softmax
Softmax最大的问题是计算量的问题,尤其是在NLP中,对于使用One-Hot编码的单词,如果单词有20000个,每次预估和训练,都要对计算20000次指数幂,运算量相当大,有人提出了Hierachical Softmax来解决这个问题,本质上是使用一棵树来取代softmax.
常见神经网络
最简单的神经网络就是LR了。
y
^
=
s
i
g
m
o
i
d
(
∑
i
=
1
n
w
i
x
i
)
\hat y = sigmoid(\sum_{i=1}^nw_ix_i)
y^=sigmoid(i=1∑nwixi)
但是这个只有一层,没法解决异或问题,并且没有考虑二维特征交叉问题。
在推荐系统中FM可以解决特征交叉问题,并且不会导致引入二维特征交叉之后的稀疏问题。实际上FM就是一种降维,也是一种计算embedding的方式。
NLP
总领链接
NLP作为一个单独部分
distributed representations(词向量) 是相对于one-hot来说的,不是在一个特定的位置放一个1来表示,而是使用连续值的向量来表示,所以词向量是一个意译。这样做的好处有两个:1. 降低表示一个单词所使用的维度,从而降低训练和预测的计算量;2.能够表示不同单词的相近程度,包括词性、近义词等。
Word2Vec
这篇文章介绍了word2vec的精髓,简明扼要,CBOW和Skip-Gram都有讲到,相当直观了。但是现在看NLP里主要的文章都是拿来做这种概率分析,指望这个怎么做语义分析和和语句生成?太原始了吧?就这?
Attention
一些有意思的问题
1. 神经网络使用的活化函数一般是单调的,神经网络可以预测非单调函数吗(比如二次函数)?
答案:可以,但是要保证网络结构满足条件。
以预测二次函数
y
=
f
(
x
)
=
x
2
y=f(x)=x^2
y=f(x)=x2为例,我们可以构建一个
1
×
N
1
×
N
2
×
1
1 \times N_1\times N_2\times 1
1×N1×N2×1的网络,则
{
h
1
=
σ
(
∑
i
=
1
i
=
N
1
w
i
(
1
)
x
+
b
i
(
1
)
)
y
ˉ
=
σ
(
∑
i
=
1
i
=
N
2
w
i
(
2
)
h
1
+
b
i
(
2
)
)
\begin{cases} h_1=\sigma(\sum \limits _ {i=1}^{i=N_1}w_i^{(1)}x+b_i^{(1)}) \\ \bar y=\sigma(\sum\limits_{i=1}^{i=N_2}w_i^{(2)}h_1+b_i^{(2)}) \end{cases}
⎩⎪⎪⎨⎪⎪⎧h1=σ(i=1∑i=N1wi(1)x+bi(1))yˉ=σ(i=1∑i=N2wi(2)h1+bi(2))
为了简化,我们假设第一层只有两个神经元,如果第一层的第一个神经元权值
w
1
(
1
)
>
0
,
b
1
(
1
)
<
0
,
w
2
(
1
)
<
0
,
b
2
(
1
)
<
0
w_1^{(1)}>0, b_1^{(1)}<0, w_2^{(1)}<0, b_2^{(1)}<0
w1(1)>0,b1(1)<0,w2(1)<0,b2(1)<0。此时,对于
x
>
0
x>0
x>0。因为权重和偏置都为负,神经元
A
2
A_2
A2进入了饱和区,没有区分度,但是神经元
A
1
A_1
A1可以处在非饱和区,有区分度,且保证x和y有正相关关系。同样的,对于x<0,神经元
A
1
A_1
A1处于饱和区,神经元
A
2
A_2
A2处于非饱和区有区分度,且且保证x和y有负相关关系。
def test_backpropagation(self):
# 实现定制化模型,来看看各个层上的权重变化
input = Input(shape=(1,))
x = input
h1 = Dense(2, activation='sigmoid', )(x)
y = Dense(1, activation='sigmoid', )(h1)
model = Model(inputs=input, outputs=y)
model.compile(optimizer=RMSprop(learning_rate=0.0001), loss=MeanSquaredError(),
metrics=['AUC', 'MeanSquaredError', 'BinaryCrossentropy'], )
model.summary()
to_raw = lambda np_arr: list(map(lambda np_ele: np_ele.tolist(), np_arr))
weights = model.get_weights()
print(f" weights type {type(weights)} and element type {type(weights[0])} and element 0 {weights[0].tolist()}")
print(f"model weights: {to_raw(model.get_weights())}")
x_train = np.linspace(-1, 1, 100)
y_train = x_train ** 2
print(f"x is {x_train} and y is {y_train}")
tensorboard = TensorBoard()
model.fit(x_train, y_train, epochs=90000, callbacks=[tensorboard])
y_pred = model.predict(x_train)
print(f"predicted: {model.predict(x_train)}")
print(f"model weights: {to_raw(model.get_weights())}")
plt.plot(x_train, y_train)
plt.plot(x_train, y_pred)
plt.legend(labels=('y_train', 'y_pred'))
plt.show()
模型权重和之前推导并不一致。因为之前只是一种可行解,以下解也是可以的。
[[[-4.884231090545654, -5.03098726272583]], [-3.347017765045166, 3.366368532180786], [[6.211307525634766], [-6.000507831573486]], [2.83687686920166]]
一个预测的示例