写在前面:我将从一个入门者的视角(水平)将机器学习中的经常使用算法娓娓道来。自身水平确实有限,若是其中有什么错误的话但愿你们指出,避免误导你们。而后这是这个系列的第二篇了,对于初学者来讲,若是你没看过第一篇,推荐看看机器学习基本算法之线性回归,里面说起到了不少基础的数学知识和一些的机器学习思惟,对于理解这篇文章颇有帮助,不少机器学习流程化的东西这里就不在具体介绍为何这样作了,而是直接解释为何在逻辑回归中须要使用这样的方式来实现,更加聚焦于逻辑回归实现自己,而非机器学习的流程理解。html
先上效果图 ^_^git
Python: 3.5github
TensorFlow: Anaconda3 envs算法
IDE: PyCharm 2017.3.3编程
若是你看过前面的线性回归介绍,能够知道,线性回归是经过对一类呈线性分布的数据进行拟合,而后训练一个线性模型对数据进行预测。那么在逻辑回归中,能够暂时理解为处理分类问题。好比给出一我的的经纬度,要判断这我的在哪一个国家,若是仅仅以线性回归的思惟,咱们很难根据经纬度去构造出一个很好的线性模型去预测地区,咱们就须要知道的是国家分界线,而这些分界线的得到就是经过逻辑回归。app
Sigmoid
若是你尚未太理解逻辑回归处理的问题和线性回归比的独特性,咱们再来举个小例子(注意:为了简单,这里的数据都是特殊取值的),来引出今天的主角:机器学习
对于上面这张图,很明显,怎么画直线也很差,总会产生较大的偏差。而若是有一个函数可以表示:函数
那么就完美符合当前的数据分布,直觉告诉咱们有很大可能其对数据的预测能力也比较强。接下来,就是找一个 形函数来做为拟合函数。这里很明显咱们的函数不是线性的。这里提出一个叫
的函数:学习
从图像中能够看到,这个函数的完美符合咱们心中的预期。
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(x)
train_Y = np.asarray(y)
fig = plt.figure()
plt.xlim(-1, 12)
plt.ylim(-0.5, 1.5)
plt.scatter(train_X, train_Y)
s_X = np.linspace(-2, 12, 100)
s_Y = 1/(1 + np.power(np.e, -6*(s_X - 5)))
plt.plot(s_X, s_Y)
plt.savefig("linear2LogisticreGressionGraphAddSigmoid.png")
# plt.savefig("linear2LogisticreGressionGraph.png")
plt.show()
复制代码
前面已经说了,为了更好体会逻辑回归,因此数据有点特殊和单一,下面咱们再来看一组数据:
像这种有明显的聚拢效果的散点图,能够看作是一个分类问题,因此对于这里咱们怎么利用前面提到的逻辑回归来处理相似于这种分类问题?
这里思惟要稍微转换一下,之前咱们以 的值做为输出,这里不是了,这里须要预测的结果是给定
求出该点是红色的圆形仍是蓝色的三角形?因此要找一个形如
的函数来处理,前面的
函数能够帮咱们分类,只要假设
和
各表明一种状况,那么得出
时就能够推断出
如今的状况是红色的圆形仍是蓝色的三角形,如今的状况是,如何将
预处理的结果与
函数联系起来。可能你已经想到了,将
的值域当作
的定义域。那么给定一个
咱们就能得出一个在区间
内的值。这下,函数就可以知足咱们的要求了。
模仿前面机器学习的思惟,咱们对于给定的输入统一用一个输入矩阵 表示
而后加入权重
,则得出函数
,接下来咱们要定义一个模型去区分这种分布?若是你看过代码就知道,在作数据的时候默认就是利用
来分的类,因此天然取的是线性模型做为首选。至于怎么选模型我认为最简洁的表示每每就是最好的,下面看看吴恩达老师的例子体会一下这个过程。
既然是线性,那么直接得出 这里提醒下加
是由于咱们默认的向量都是列向量。 理解了这里那就好办了,综合咱们的
能够得出拟合函数:
既然咱们都已经把数据作了处理,因此对于输入是二元仍是一元没差了,就是一个 而已。下面咱们根据以前线性函数的例子,找一个适合描述偏差的函数。看图:
下面咱们先只分析为 的点如何经过
的关系找到一个函数来表达损失。首先能够确定的是,要对
的惩罚力度是最大的。由于从图中看出对它的预测最离谱,因此损失函数要是一个递减函数,而后继续分析损失函数是否对于
的变化呈均匀变化,也就是这个递减函数的递减幅度是设么样的?很明显,
越靠近
,它的惩罚力度应该和指数增加有点相似。越小一点点,惩罚力度马上激增,这样对于最后损失函数收敛时的参数拟合结果比较符合大多数数据的状况,不会出现它极端的分类,由于对极端的数据处罚力度实在是太大了。好了,这个函数符合三个特色,
递减
、导数绝对值递减
(递减幅度要减少)和定义域在 内的跨度必须大,最好是从正无穷到
由于若是函数值为
咱们就不要对这个点进行惩罚了,因此确定是要有
这个函数值的。这时你可能想到了,对数函数正好知足状况。因此有以下结果:
这里就不贴函数图了,下面你们本身分析一下 的状况。本身实在想不出来再往下阅读吧,若是直接看缺乏思考过程对本身的理解可能有点欠缺。就像不少书不少博客都是直接给公式,虽然勉强也能看懂,可是更多时候是本身记住,对于其中的思考过程没有一点本身的体会和心得。给个图你们本身猜猜:
下面直接给出最后的结果:
还记得前面说的逻辑回归能够看作是分类问题吗?而对于分类问题的预测咱们能够看作几率来计算。因此函数 能够看作,在
下,输入
取值为
的几率。而咱们要作的就是求出一个
是的对于给定的
的几率尽量和
符合。这时候你会发现,结果
服从伯努利分布,也就是随机变量
的取值只能是
,在
看作是取
的几率的状况下,当这个值大于
咱们认为分类为
。
好了,把上面的东西都“忘掉”,接下来进入一个几率论的思惟模式。假设这一个平面内有无数个点,而咱们给出的数据只是一个抽样数据。可是咱们不可能把全部的状况都列举出来,因此咱们要作的就是经过给出的样原本估测总体分布。其实这也和机器学习须要大量样本训练有关,很明显数据量越大,偶然偏差越小,样本的分布越接近总体分布。收回到几率论,我发现容易写着写着就跑偏了😂😂😂。如今咱们统计数据的状况是:符合伯努利分布。而这种知道分布的求可以和分布匹配的参数的方法几率论中有一个参数估计方法叫:极大似然估计
。若是不懂看一下简短介绍:
极大似然估计,通俗理解来讲,就是利用已知的样本结果信息,反推最具备可能(最大几率)致使这些样本结果出现的模型参数值! 换句话说,极大似然估计提供了一种给定观察数据来评估模型参数的方法,即:“模型已定,参数未知”。
依照上面的意思那很明显,咱们是把 那天然就有:
这是单个 的几率。若是把整个样本看作
次的独立重复实验呢?那发生的几率就是这个的乘积了。这里使用
向量表示整个数据。即:
能够看出这是一个关于 的函数,如今比较玄学的部分来了,咱们认为,这个事件既然发生了,那么它发生的几率就应该是全部事件中几率最大的,若是不是那它凭什么发生?哈哈,就是这么讲道(xuan)理(xue)。最大值?那就好办了,直接求导?不行不行,太难了。。。高中最经常使用的把戏:乘变加,用对数。而后乘以
就编程求最小值了。
前面已经给出了损失函数,咱们再次利用梯度降低算法更新权重。若是本身手写代码来实现,咱们仍是要求偏导的😂😂😂
高数的东西,这里偷懒了,字有点难打就直接截图了,毕竟这不是什么难点:
那么更新函数就是:
注意这里的 ,
表明的是一个列向量,
表明第
列第
个数字。其实仔细想一想就能够发现,更新的
就是一个数,对它求偏导的时候余出来就是和它相乘的那个
。因为前面都是从单个点开始研究,为了好理解因此直接使用下标表示,可是在使用线性边界函数的时候要有一个矩阵的思想。这里就不细讲了,后面多元的就必须使用向量形式了。
好了,基本逻辑已经搞清楚了,就是和线性回归相似,只不过这里处理的是一个线性二分类问题,须要对原始数据处理成线性值而后把线性值映射到 函数的定义域上,根据
函数特征咱们能够得出数据分类为正
的几率大小,而后进行评估。
若是对公式还有什么不懂的话,参考这篇 机器学习--Logistic回归计算过程的推导 里面讲解了如何向量化,若是你对线性代数不熟悉能够本身去看看这篇文章的讲解。下面我只讲解代码,就不对原理重复介绍了。
须要的库和数据,这里为了获取更好的精度最好使用 float64
:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(np.row_stack((np.ones(shape=(1, len(x))), x)), dtype=np.float64)
train_Y = np.asarray(y, dtype=np.float64)
train_W = np.asarray([-1, -1], dtype=np.float64).reshape(1, 2)
复制代码
定义 和
函数
def sigmoid(X):
return 1 / (1 + np.power(np.e, -(X)))
def lossfunc(X, Y, W):
n = len(Y)
return (-1 / n) * np.sum(Y * np.log(sigmoid(np.matmul(W, X))) + (1 - Y) * np.log((1 - sigmoid(np.matmul(W, X)))))
复制代码
实现参数更新(梯度降低算法)
def gradientDescent(X, Y, W, learningrate=0.001, trainingtimes=500):
n = len(Y)
for i in range(trainingtimes):
W = W - (learningrate / n) * np.sum((sigmoid(np.matmul(W, X)) - Y) * X, axis=1)
复制代码
这里的一个重点是把以前的分析向量化了,而后注意维度变化就很简单了。其中 是一个值得注意的点,它表明求某个维度的和。
其实到这里整个算法就算是完成了。下面咱们进行可视化,不会的能够参考我以前的文章 Matplotlib 保存 GIF 动图,下面给出效果和源代码:
# coding: utf-8
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
x = [1, 2, 3, 4, 6, 7, 8, 9, 10]
y = [0, 0, 0, 0, 1, 1, 1, 1, 1]
train_X = np.asarray(np.row_stack((np.ones(shape=(1, len(x))), x)), dtype=np.float64)
train_Y = np.asarray(y, dtype=np.float64)
train_W = np.asarray([-1, -1], dtype=np.float64).reshape(1, 2)
def sigmoid(X):
return 1 / (1 + np.power(np.e, -(X)))
def lossfunc(X, Y, W):
n = len(Y)
return (-1 / n) * np.sum(Y * np.log(sigmoid(np.matmul(W, X))) + (1 - Y) * np.log((1 - sigmoid(np.matmul(W, X)))))
Training_Times = 100000
Learning_Rate = 0.3
loss_Trace = []
w_Trace = []
b_Trace = []
def gradientDescent(X, Y, W, learningrate=0.001, trainingtimes=500):
n = len(Y)
for i in range(trainingtimes):
W = W - (learningrate / n) * np.sum((sigmoid(np.matmul(W, X)) - Y) * X, axis=1)
# for GIF
if 0 == i % 1000 or (100 > i and 0 == i % 2):
b_Trace.append(W[0, 0])
w_Trace.append(W[0, 1])
loss_Trace.append(lossfunc(X, Y, W))
return W
final_W = gradientDescent(train_X, train_Y, train_W, learningrate=Learning_Rate, trainingtimes=Training_Times)
print("Final Weight:", final_W)
print("Weight details trace: ", np.asarray([b_Trace, w_Trace]))
print("Loss details trace: ", loss_Trace)
fig, ax = plt.subplots()
ax.scatter(np.asarray(x), np.asarray(y))
ax.set_title(r'$Fitting\ line$')
def update(i):
try:
ax.lines.pop(0)
except Exception:
pass
plot_X = np.linspace(-1, 12, 100)
W = np.asarray([b_Trace[i], w_Trace[i]]).reshape(1, 2)
X = np.row_stack((np.ones(shape=(1, len(plot_X))), plot_X))
plot_Y = sigmoid(np.matmul(W, X))
line = ax.plot(plot_X, plot_Y[0], 'r-', lw=1)
ax.set_xlabel(r"$Cost\ %.6s$" % loss_Trace[i])
return line
ani = animation.FuncAnimation(fig, update, frames=len(w_Trace), interval=100)
ani.save('logisticregression.gif', writer='imagemagick')
plt.show()
复制代码
先从三元分类开始了解,加入给了如下分布,要你求边界:
比起前面的来就难多了,首先前面是线性分类的而这里很明显不是,其次这里又多出了一个变量。。。别急,对比一下三幅图你就清楚了。
这里咱们就解决了多一个变量的问题,经过选取一个主变量为 ,其它的值就为
,而后就能够进行二分。还有一个问题是如何选取参数?咱们既然能够转换成二分问题了,那么对于每一次二分都只要选取
了。
很明显有两个基本的
可是为了能过选取更加合适的边界,若是只是这两个特征的话是不够的,因此其它的
等等,看你要拟合成什么样的边界就选取什么样的特征,同理三维就选取一个能表达弯弯曲曲的平面的特征量。下面选取权重,也就是特征前前面的系数,若是特征选取好了,那么只要在此基础上加一个
就行了。
下面咱们来举个小例子如何操做:
很明显这里有一个包围趋势,因此理所固然想到圆。也就是说选取的 那么
函数就能够定义为:
至于解法就和上面相似了,把这些都抽象成向量,那么至于什么维度,有几个变量都没差了。须要记得的是,这里要跑的次数是分类的种数的大小,也就是把每一种都当作主角处理一次获得属于它把其它元素区分的分界线,预测是也要把全部模型跑一遍,哪一个模型的几率高就取哪一个做为分类预测的结果。
这里就不详细介绍应用了,之后有机会补充代码实践,下面给出几篇参考博客:
求解最优化问题不仅是只有梯度降低算法,还有其它可以收敛更快的方式。好比:共轭梯度法 BFGS
(变尺度法) 和 L-BFGS
(限制变尺度法) 等,不过我如今还没看过这些算法的具体实现,这里就立下 FLAG 了。不过通常咱们使用这些算法都是调用现成的库,毕竟本身写的算法多多少少仍是有缺陷的,
在以前的例子中,你会发现个选取好特征量以后,怎么选取迭代次数很重要。举个小例子:咱们拟合一个符合二次函数分布的散点图,若是你选取的模型是 那怎么跑都跑不出理想结果,必须把
这个特征项加上,这样就不会欠拟合。一样,若是你选取好了一个二次模型,可是迭代次数不够,也会致使欠拟合,讲道理若是模型预测和分布一致,那么迭代次数天然是越多越好。但可是,若是你不能肯定,又加一个
的特征量进去,原本是数据是符合二次函数的特征,若是训练次数控制得比较好,也能够训练出好模型,无非就是把
的权重置零,若是训练次数控制很差,那么必将形成欠拟合(训练次数过少)或过拟合(训练次数过多),这里不是说损失函数值越小越少,而是说那拟合函数的趋势符合总体数据的趋势,从几率论的角度讲,就是咱们抽取的是整体中的一个样本,因为得到全体数据不现实,因此只能使用样本估计总体,因此这里的估计的就应该是一个趋势。
那么如何避免过拟合或欠拟合呢?我以为能够从一下角度考虑:
总之这里最最关键的是模型的准确度,若是一开始模型选错那怎么迭代也不会跑出好的效果。
下面给出吴恩达老师的课件图,你们体会一下:
能够看到,咱们还有一种方式处理:正则化
我根据我本身的理解来大胆解释一下吧,若是你有本身的理解就能够不看了。。避免误导。正则化通常就是解决过拟合,也就是咱们根本不能肯定趋势,因此添加了不少多余的特征量,那么对于这个模型什么样的结果是咱们能够接受的呢?若是那些多余特征量的权重接近 是否是就意味着咱们也能获得较好的拟合效果,甚至和没有这些干扰特征是达到同样的效果。这里就是对那些特征量的系数进行处罚达到这个目的的。
从上面的解释能够看出,咱们不须要对 进行惩罚,因此结果就是在损失函数后面加上
。固然这只是其中一种方式,你能够选取其可行的方式,主要目的是避免权重过大,拟合的曲线过于弯曲(由于求导以后,斜率和系数是正相关的)。这里的哲学思想就是在拟合结果可以接受的状况下,曲线越平滑,容错能力越高,样本越可以表明整体。
这里就直接截图举例子了:
正则化线性回归
正则化逻辑回归
通过一些细节的阅读,对整个过程的了解又加深了很多。动手实践才是最好的方式,以前觉得本身公式看懂了就好了,而后此次写代码是把一个变量写在了括号里面,死活拟合不出效果。而后花了几个小时以后决心本身再推一遍公式而且从新敲一下公式代码(由于打印数据发现很诡异),后面就一遍过了。因此若是你也是刚刚开始学习,哪怕照着敲一遍,以后也要本身将思路整理成文字/代码形式,最好是能整理成博客,方便之后本身复习。
仍是很喜欢那就话:花时间弄懂细节就是节省时间
最后:Happy Year Of Dog ^_^ !