前言
人工智能,AI的概念在22/23年随着chatgpt的诞生而爆火,而事实上AI技术在我们日常生活中早已得到广泛应用,从日常使用各类APP,到坐火车飞机。基本上你能想到的日常生活场景都能有AI的一席之地。作为一名研发工程师,我们不需要去深究各种复杂的人工智能模型到底是如何搭建的,但了解人工智能的基本原理对我们对于AI的应用是有帮助的,这里用最简单的方式为大家介绍常用人工智能算法实现原理,保证高中数学水平就能看懂。
什么是人工智能
人工智能(Artificial Intelligence,简称AI)是指利用计算机技术来模拟、扩展和承担人的智能活动的一种技术和科学。人工智能技术可以实现和模拟人类的思维、学习、推理、判断、决策、语言、知识获取和处理等智能活动,具有自主、自适应、自我学习和推理、自我控制等特征。人工智能是目前计算机科学领域的重要研究方向之一,应用广泛,包括自然语言处理、机器翻译、图像识别、语音识别、智能游戏、智能家居、智能医疗、智能交通等领域。
从本质上讲,人工智能要解决的问题绝大部分可以总结为预测结果,而从技术手段来看,人工智能算法大部分可以归类为曲线拟合(观点有争议,详情大家可以看下这个问答)
为什么说人工智能主要解决的是预测问题。我们日常使用的大部分人工智能算法,包括搜索推荐,图片识别,甚至于chatgpt这类通用语言模型都在解决一个问题,即在一个业务场景下给一个输入值,计算机预测输出值应该是什么。以chatgpt为例,用户在输入提示词后,chatgpt做的事情可以理解为根据从网络上获取的问答和文本计算出每个词可能出现的概率,并选出概率最高的一个词作为预测结果输出,同时将前面的输出当作输入再次计算概率并最终生成文本。
举一个具体的例子,我们向chatgpt输入“胖胖是最帅的人吗?”chatgpt计算出“是”这个词出现的概率是50%,“否”出现的概率是20%、“一般般”的概率是15%、“钝角”的概率是10%...,在计算完所有词的概率之后,chatgpt发现“是”的概率最高,因此输出“是”,这时候它会再将“胖胖是最帅的人吗?是”作为输入,chatgpt计算出下个词为空的概率最高,就会结束内容生成。
为什么说人工智能算法大部分可以归类为曲线拟合呢,实际上大部分机器学习算法都是在构建一类模型,这类模型通常会根据已有数据训练出一系列参数,输入值和这些参数做运算后就能得到我们上面说到的预测结果,由于这些参数构成的模型画在坐标系中可以看做一条曲线(1维的情况下),而机器学习算法要做的其实就是找到最能拟合现有数据的曲线,从而能对未知的数据进行预测。
解决预测问题
这里我会用一个让机器自己学会预测房价的例子让大家直观的感受人工智能是如何通过“曲线拟合”来解决现实中的“预测”问题的。实际情况下,预测房价需要的因素有很多,地理位置、朝向、房间个数、面积、层高等,这里为了方便讲解,我们只考虑房屋面积这一个因素,因此读者只需要高中数学水平就能理解这个算法。
问题定义
在这个例子中,我们会让机器学会从一些房价成交数据中找到规律,并且能够自主的根据面积预测房价,在机器学习领域中,这类算法被称为监督学习算法,即通过已有的数据进行训练,实现分类数据或预测结果的功能。我们已有的房价数据格式如下(详情可以见附录)
房屋面积(㎡) |
房屋售价(万) |
88.64 |
305 |
88.75 |
350 |
117.12 |
312 |
... |
... |
数学建模
有了数据以后,计算机要怎么开始学习呢?我们首先要定义问题,即用数学语言对问题进行描述(这种过程也被称为建模)。为了直观理解,我们直接在二维坐标系中画出这些数据。

通过观察数据,我们可以做出一个假设,即房屋面积和房屋价格之间呈线简单的性关系,我们可以直接在坐标系中划一条线,这就是我们的模型,如下图所示。

通过这个模型,计算机就可以直接对房价预测。例如一个人的房屋面积是140平米,那么我们可以告诉他,他大约能以535万的价格出售。用高中数学的知识来描述这个模型其实就是一个二元一次函数,(x为房屋面积,函数值为房屋售价)

而实现准确预测的前提是我们需要找到最佳的 参数(即找到最拟合房屋成交数据的直线)。
代价函数
如何评估两个参数的效果呢?在这个场景中,我们可以用已有训练数据的实际值到预测模型的预测值之间的平均距离来定义模型拟合效果,平均距离越小,代表模型越精准,具体计算方式如下,每个红线长度代表一个实际值到预测值的距离

由于求绝对值不便于计算,我们用求平方除以2来替换,同时用稍微书面化一些的语言来描述这个函数
(表示第i行数据的预测值,
表示第i行数据的实际值,m表示训练数据总行数)
我们稍微简化一下就成了如下形式(没学过累加符号不要慌,后面会有更简单的方式)
这个二元二次函数我们称之为代价函数,函数的结果用于衡量模型的预测准确度,对应的函数结果越小,模型预测越精准。此时,我们要解决的问题变成了找到使代价函数最小的
。下面是我们用不同的
得到的模型以及计算出的代价函数的值。
当然,上面这个函数看起来还是有点复杂,我们可以直接把房屋成交数据带入代价函数并简化可以得到如下结果
这个二元二次函数在坐标系中是一个曲面,我们要找的就是这个曲面的最底部,即z最小值对应的
这个时候有同学可能会问,二元二次函数我高中没学过啊?没关系,观察上图我们可以发现,代价函数的值几乎只与有关,而与
几乎无关(由于面积为0的时候房屋价格一定为0,因此线性模型下
应该为0),将
代入后函数就会简化为二元一次。
这个函数我们就非常熟悉了,在坐标系中的表现形式是一条曲线,现在问题终于变成了一道高中数学填空题,找最二元一次函数的最小值
梯度下降算法
高中数学告诉我们,求出函数导数为0的解就可以找到最小值(最大值),但要注意,我们这里为了讲解,只列举了最简单的例子,实际的人工智能模型中参数通常会多达成千上万个,而且通常不会采用简单的线性模型,计算机是无法针对如此复杂的函数求解的,因此我们可以选择一种更适合计算机的方式,梯度下降算法。
我们可以这么理解梯度下降算法,在上述的函数图中随机找一个点,不断以固定步长朝着函数值变小的方向移动,最终找到最小值。在二元函数中,函数值变小的方向可以理解为求导数
如上所示,在第一张图中选取一个点,求导数(这里可以看做求斜率),发现导数大于0,则向左移动一个步长,迭代足够次数后,求得最小值,这里给出求导公式(其实是微分公式)
化简后得到
看起来很复杂,其实将上面代价函数以及房价数据全部带入后得到
算法实现
def getThetaSimple(x_array, y_array, times, step):
#先随机选取一个theta
theat = 10
for i in range(times):
print(theat)
theat -= compute_grad(theat, x_array, y_array) * step
return theat
def compute_grad(theat, x_array, y_array):
cost_grad = 0
for x_index in range(len(x_array)):
x = x_array[x_index]
y = y_array[x_index]
cost_grad += (theat * x - y) * x
return cost_grad / len(x_array)def getThetaSimple(x_array, y_array, times, step):
#先随机选取一个theta
theat = 10
for i in range(times):
print(theat)
theat -= compute_grad(theat, x_array, y_array) * step
return theat
def compute_grad(theat, x_array, y_array):
cost_grad = 0
for x_index in range(len(x_array)):
x = x_array[x_index]
y = y_array[x_index]
cost_grad += (theat * x - y) * x
return cost_grad / len(x_array)
我们运行一遍迭代一千次的算法,并将每一次迭代的到的画在坐标系中,图像如下
可以看到随着迭代次数增加,的值逐渐收敛至3.96左右,至此,得出用于预测房价的模型。
需要注意的是,我这里为了方便大家理解只是举了一个及其简单的案例,在实际情况中通常不会存在仅有一个特征值的情况,模型也通常不会是简单的线性模型,这时候会应用到的高次多维函数求导以及矩阵运算就不是高中数学水平能够理解的了,这里附上支持任意维度数据的梯度下降算法实现供大家参考。
#支持任意参数数量的梯度下降算法
#注意这里为了支持任意参数并且支持矩阵运算,输入参数会人为添加一个0维度
def getTheta(x,y,times,step):
a,b = np.shape(x)
train = np.ones((a,b+1))
m, n = np.shape(train)
train[:,1:] = x
theta = np.zeros(n)
for i in range(times):
hp = np.dot(train,theta.transpose())
error = hp - y
grand = np.dot(train.transpose(),error)/m
theta = theta- step*grand
return train,theta#支持任意参数数量的梯度下降算法
#注意这里为了支持任意参数并且支持矩阵运算,输入参数会人为添加一个0维度
def getTheta(x,y,times,step):
a,b = np.shape(x)
train = np.ones((a,b+1))
m, n = np.shape(train)
train[:,1:] = x
theta = np.zeros(n)
for i in range(times):
hp = np.dot(train,theta.transpose())
error = hp - y
grand = np.dot(train.transpose(),error)/m
theta = theta- step*grand
return train,theta
解决分类问题
前面讲解房价预测的例子中我们用的就是线性回归算法,这种算法比较适用于预测具体的值。而针对机器学习中的另一类问题-分类,例如根据年龄大小判断是否结婚,我们就需要采用另一种有些类似的算法-逻辑回归来实现。
数学建模
相比较线性回归,逻辑回归的最大变化是在线性模型的基础上增加Sigmoid函数其中z为前面讲的线性模型的输出值,因此我们也可以写作
,以下是Sigmoid函数的图像,可以看到这个函数最大的特点是y值被严格限定在[0,1]的范围内,且x大于0时y大于0.5,x小于0时y小于0.5。
这样模型预测的结果从求具体的值变成了求某一分类的概率。我这里以根据年龄判断是否结婚为例子,判断结果只会有已婚和未婚两种,在上面的模型中,如果给定一个年龄对应输出值是0.7,说明用户已婚的概率是0.7,未婚的概率则是0.3,模型判定用户已婚。
数学模型的直观解释
看到这里你可能一头雾水,为啥套了个函数就变成求分类的概率了?我这里简单解释一下上面的sigmond函数到底做了什么事情。假设我们现在有一系列可以分为两类的数据如下图所示。
图中x1和x2分别表示两个特征值(可以理解为我们现在要实现一个判断房屋是在滨江区还是萧山区的算法,并且有了一系列不同面积和售价的房屋数据,其中x1为面积,x2为售价),基于简单线性模型的输出作为sigmond函数输入我们构建了逻辑回归模型,得出的方程如下
这里我们假设参数是,前文讲过,sigmond函数的特性就是y值被严格限定在[0,1]的范围内,且x大于0时y大于0.5,x小于0时y小于0.5,因此当Z值(也就是线性模型输出值)大于等于0时,sigmond函数值将大于等于0.5,我们这里将大于或等于0.5的结果分类为1(滨江),反之分类0(萧山)。因此将参数带入模型很容易得出,当
时,结果分类为1(滨江),否则分类为0(萧山),因此
就是两类数据的分界线,越远离这条线,分类正确的概率越高,这就是sigmond的意义。
当然现实情况下分类问题通常不会这么简单,可能还会有一些比较复杂的数据,比如如下图所示的数据分布
这时候我们需要采用一些比较复杂的模型,例如高次模型作为输入。这个模型在
值分别为[-1,0,0,1,1]时我们得到的是一个半径为1的圆
代价函数
线性回归的代价函数定义可以应用到逻辑回归算法中,但是带入后会发现该函数是一个非凸函数,意味着代价函数存在许多的极小值,我们在这类函数中应用梯度下降算法时程序可能会停在其中某一个极小值(局部最优解)而不是找到全局的极小值(图中红线标出的部分)
因此在逻辑回归中,可能使用的代价函数是这样的,其中
在坐标系中画出与
之间的关系如下图所示:
这样构建的函数的特点是:当实际的
且
也为 1 时误差为 0,当
但
不为1时误差随着
变小而变大;当实际的
且
也为 0 时代价为 0,当
但
不为 0时误差随着
的变大而变大。
套公式就能实现的机器学习算法
贝叶斯公式
前面讲过机器学习算法大部分在做曲线拟合,但是凡是都有例外,比如这一节要讲的朴素贝叶斯算法。贝叶斯算法的鼎鼎大名我这里就不过多介绍了。作为一种能应用在几乎所有你能想象的生活场景中的统计方法,它的公式其实只需要初中水平就能看懂
名词解释:P(A|B)是在B发生的情况下A发生的概率;P(A)是A发生的概率;P(B|A)是在A发生的情况下B发生的概率;P(B)是B发生的概率。
在贝叶斯算法产生前,人们只会计算正向概率,只能解决【已知一个箱子里有3个白球7个黄球,摸出黄球的概率是多少】这类问题,而贝叶斯算法让我们有了能够解决【摸了出1个白球,3个黄球,计算箱子里白球和黄球的比例】这类计算逆向概率的方法。前面我们讲过,人工智能要解决的根本问题就是预测,因此擅长基于已有数据反推概率的贝叶斯算法在人工智能领域应用相当广泛。例如判断是否垃圾邮件,判断是否恶意评论等都可以用到它。
简单应用
我们这里用一个判断恋爱情况的案例简单介绍朴素贝叶斯算法的原理
问题:假设我们已经有了如下数据,我们如何判断一个【有房,没车,抽烟,非常丑,非常高】的人是否单身?
是否有房 |
是否有车 |
是否抽烟 |
帅不帅 |
高不高 |
|
有 |
有 |
否 |
丑 |
非常高 |
非单身 |
有 |
有 |
否 |
丑 |
不高 |
单身 |
无 |
无 |
否 |
一般 |
非常高 |
单身 |
有 |
无 |
是 |
丑 |
非常高 |
单身 |
有 |
无 |
是 |
非常帅 |
一般 |
非单身 |
无 |
无 |
是 |
非常帅 |
一般 |
非单身 |
有 |
有 |
是 |
一般 |
一般 |
非单身 |
有 |
无 |
是 |
一般 |
非常高 |
非单身 |
上述问题其实可转换为求有房,没车,抽烟,丑,非常高的前提下单身和非单身的概率,概率更高的就是我们的预测结果,这里直接将问题带入公式
P(单身|有房,没车,抽烟,非常丑,非常高) = P(有房,没车,抽烟,丑,非常高|单身) * P(单身) / P(有房,没车,抽烟,丑,非常高)
直接拿数据套公式计算
P(有房,没车,抽烟,丑,非常高|单身) = 2 / 3 * 2 / 3 * 2 / 3 * 2 / 3 * 2 / 3 = 32 / 243
P(单身) = 3 / 8
P(有房,没车,抽烟,丑,非常高) = 3 / 4 * 5 / 8 * 5/ 8 * 3 / 8 * 1 / 2 = 225/4096
则有房,没车,抽烟,丑,非常高的前提下单身的概率为(32 / 243 * 3 / 8) / (225/4096)
通过同样的方式计算出不单身的概率为(96/3125 * 5 / 8) / (225/4096),和单身的概率进行比较。分母相同的情况下比较分子大小(32 / 243 * 3 / 8)> (96/3125 * 5 / 8)单身概率更大 ,因此预测有房,没车,抽烟,丑,非常高的人应该是单身。
实现文本分析
相比较于线性回归逻辑回归等算法,朴素贝叶斯虽然非常容易理解,实现起来也很简单,但其应用非常广泛,语言模型chatgpt中就有应用。你可能会好奇,前面介绍的基于可量化的数据来实现机器学习算法很容易理解,文本这种难以量化的内容机器是如何分析的呢?这里基于朴素贝叶斯实现一个简单的文本分析算法来做一个介绍