CV算法恩仇录

V1

2022/08/19阅读:9主题:默认主题

初识人工智能原理

初识人工智能原理

  • 人工智能数据的几何意义
  • 通俗理解人工智能原理
  • 理解一元线性回归与梯度下降的公式
  • 通过房价预测案例理解多元线性回归
  • 理解欠拟合和过拟合

人工智能数据的几何意义

用通俗易懂的语言让你理解人工智能的奥秘!

计算机视觉目的是让计算机像人一样可以“看到”画面,并对画面做判断及处理,你有想过,计算机世界的图像是什么样的吗?我们人类在看一个东西到时候,靠的非常近可以看的非常清楚,借助一些工具放大时能看到更加微观的信息,但是如果在电脑中放大会怎么样呢?电脑上看图片,想放大看一些特别细小的地方时,会发现图像一开始还是清晰,再放大就变得模糊起来,再放大变得有“颗粒感”。例如下图:

图1: 图像像素

图片来源

这是因为图像在计算机的世界里是由像素组成,每个像素点都是用数值来表示。在计算机的世界里,“万物皆数值”,不管你是图片还是音频,在电脑中存储的都是一系列的数值,只是在不同形式的数据存储在电脑中的数值含义是不用的。

同样,人工智能处理的数据也都是数值类型,比如图像、音频等。我们理解人工智能的原理,需要先理解数据数值的表现及含义。用数值表示的数据,可以从几何的角度进行理解。我们先来看一下,“3”一个数值的含义

图2: 一维空间的数值

可以看到,一个数值表示的的数据的含义是在一维空间中的一个点。那如果是由 个数组成的数据呢?在几何空间中表示什么?是二维坐标系内的一个点。那现在有一组由 个数值组成的数据, ,我们看一下它在二维空间内的图像看一下。

图3: 二维空间的数据

图中五个点连成一条直线,图中选取了比较特殊的值,使得五个点在二维空间内呈直线状。其实每种数据在几何角度的其所在的空间内(可能是很高维的空间)都可以组成形状,虽然不太可能是这种非常简单的直线,这是数据的几何意义。

画个重点:数值表示的数据可以在几何角度理解为高维空间中的点,由足够多数据组成的高维空间中的图形是数据分布的一种形式。

通俗理解人工智能原理

问题一:请问图像和人工智能的原理有什么关系呢?

这个问题我们要从目的出发,通过学习数据完成对这类问题的预测,注意是这类问题,不局限于给定的数据。就以上面的图形为例,假设我想通过X轴的值来预测Y轴的值,为了达到这个目的,我们画一条直线。

图4: 直线

现在把这条直线的公式求出来,公式为 。这样当我们输入 时就得到了 的结果,那么换句话说,我们画的这条直线代表了图中的 个点,而且不仅这 个点,输入其他的 同样可以进行预测。当我们输入 时,得到 ,达到了预测给定的 个数据点之外的能力。

这就是从几何的角度理解人工智能,人工智能构建一个函数,通过学习数据达到函数在几何空间内的形状和数据的几何表示非常接近,实现“代替”数据的目的,从而达到预测的能力,这个函数就是算法模型

问题二:请问这个函数是怎么学习数据的呢?

算法模型在进行自我学习、自我更新的过程其实就是我们定义的函数去拟合数据,我们先通过一系列代码看一下效果,再来讨论其中的原理是什么。

我准备了点数据,这个数据相对前面咱们举得例子就复杂很多了,总共由 条数据组成。数据是二维数据,由 组成。我们的目的是用 来对 进行预测,这时,我们称 为特征, 为目标也叫 。执行下方代码,查看数据的几何表示。

# 导入 numpy 库,对矩阵数据进行操作
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# 将预设好的数据读取进来
data_path = './data.npz'
data = np.load(data_path)
# 读取特征 x
x = data['x']
# 读取标签 y
y = data['y']
# 绘制散点图
plt.scatter(x, y)
plt.show()

运行结果展示如下:

这个数据确实比刚才复杂多了,形成了一个弧形的曲线。

现在我们设计一个函数,来拟合这些数据。弄的简单一点,还是使用一条直线吧。

直线的公式为 ,这里我们给 一个初始值,假设都为 ,下面的代码是绘制一条直线,执行看看效果吧。

'''
定义线性函数
   def () 的方式是定义一个函数
'''

def func(x, a, b):
    return a * x + b

# 给 a 和 b 一个初始值,都为1
a = 1
b = 1
# 在区间 [1, 5] 内平均取 1000 个点
x_line = np.linspace(151000)
# 根据取的点套用公式得到直线
y_line = func(x_line, a, b)
# 绘制数据散点图,用作对比用
plt.scatter(x, y, s=20, alpha=0.4, label="data")
# 绘制直线
plt.plot(x_line, y_line, lw=5, color="#990000", alpha=0.5, label="predict")
plt.legend()
plt.show()

运行结果展示如下:

可见效果确实是不好。这是因为我们随便设置了个初始的直线,还没有进行学习。

问题三:请问如何让我们的模型算法进行学习与更新?

首先,我们需要确定我们要更新什么东西,在我们初中学习的时候,直线方程中 是变量, 是固定值。是在固定的直线上带入 的值求 ,这个过程很像我们模型算法去预测时的过程。但是我们现在要做的是改变直线方程,让直线方程变得更加贴近我们的数据。这时候我们要更新的就是 了,对 (斜率)和 (偏执)进行更新,会改变直线方程。

确定好要更新的变量之后,问题就是如何去更新。最直接粗暴的方式就是挨个去试 ,看看什么效果好,这种穷举的方式当然是可以,不过这可能是“有生之年”系列,能不能完成学习完全的概率比中彩票的概率还要低的多,而且对于模型算法,它也不知道什么叫学习完成。

我们回想一下,在幼年学习的时候,经常会被家长训,告诉我们什么是能做的,什么是不能做的。也就是告诉我们一个学习方向,上学阶段在学习知识的时候,也会给大家各种测试及考试,让我们知道我们的学习是不是对的,学习成果如何。

同样的,算法需要知道如何去学习,或者说怎么学习是对的怎么学习是错的,我们需要帮助模型算法构建一个”价值观“,并且像测试一样把这个正确与错误的概念量化,量化的意思就是数值化,像我们考试的分数一样。

那我们可以列出损失函数(Loss Function 或 Cost Function)的公式:

其中 表示直线函数预测的值, 表示真实值也就是label。

我们观察一下损失函数函数,可以发现和我们上学考试的分数是相反的,考试时分数越高表示学习效果越好,但是损失函数分数越低表示学习效果越好,如果损失函数为0,就表示模型算法很”完美“。

那我们的目标就变成让损失函数越低越好,但是现在的损失函数里并没有 ,我们将 带入 损失函数得到:

问题四:请问怎么求损失函数的最小值呢?

在前面的内容,函数及数据都有其几何意义,在某维空间内呈直线、曲线、曲面、立方体等。loss 函数同样可以映射到几何空间内,在我们追求减少损失函数的值的过程也是追求其在几何空间内的极小值。在例子中的损失函数上,要做的是对 a 和 b 进行优化,而 x 和 𝑦𝑙𝑎𝑏𝑒𝑙 是固定值(是训练数据,训练数据是固定的)。寻找 loss 函数的几何图形上的极小值的过程,应该是对 a 和 b 进行更新。

问题五:请问怎么对 a 和 b 进行更新呢?

是不是像我们求公式一样求出来,并不是那样,咱们的计算机计算核心只能进行数值的计算,而不能进行推理,按照咱们公式推理的形式并不能应用在计算机上。计算机只能进行数值的尝试,尝试带入不同的 a 和 b,如图5所示。

图5: 不同数值的尝试

现在剩下的问题就是,如何对 a 和 b 进行更新。在更新的过程中要确定两件事,第一件事就是往什么方向进行更新,第二件事就是要更新多少。

图6: 导数

图6中红色直线是 处二次函数的切线,而该点的导数 表示的方向是图中红色箭头方向,为了得到极小值点,需要沿着导数的反向更新。

而导数(切线斜率)的大小表明变化的速率,意思是当导数(切线斜率)比较大的情况下,变量变化所引起的结果变化也就更大。把这个概念引入求损失函数的极小值的问题上,就是当 的偏导数比 大时,说明在当前情况下参数 更加重要(或者说参数 偏差的更大),需要对 进行更大的更新幅度。划重点,以变量偏导数乘以一个系数作为变量更新的数值,这个系数我们叫它学习率(learning rate),这个系数能帮助我们一定程度上控制算法模型的自我更新。

回到前面咱们拟合数据的例子中,首先先求一下损失函数对分别对参数 和参数 的偏导数。先把损失函数展开。

根据偏导数公式,求参数 的偏导数表达式 与参数 的偏导数表达式

是参数 的偏导数表达式,有了表达式,就可以在代码中定义求参数 和参数 的导数的方法了。接着刚才的代码,下面我们看看怎么定义 的偏导方法。

# 定义偏导数函数
def grad_func(x, d, a, b):
    '''
    根据当前输入的x、d、a、b,求a和b的偏导
    Arguments:
        x:输入的x
        d:真实值  label  y_label
        a:当前a的值
        b:当前b的值
    '''

    y = func(x, a, b)
    # a的偏导数
    grad_a = 2 * (y - d) * x
    # b的偏导数
    grad_b = 2 * (y - d)
    return grad_a, grad_b

有了导数公式,就可以尝试对我们的直线函数进行拟合,具体为,每轮带入训练数据x,用直线函数求得的 与数据中 带入偏导数函数,求得 ,将 分别乘以学习率就是这次要更新的实际数值,因为是要沿着导数的反方向更新,所以用 完成一轮更新。

# 学习率
eta1 = 0.05
# 每轮学习数据量
batch_size = 32
# 进行1000次循环,也就是学习1000轮
for itr in range(1000):
    # 随机获取batch_size个数据的index
    idxs = np.random.randint(0, len(x), batch_size)
    # 取出随机的batch_size个数据
    inx = x[idxs]
    # 取出随机的batch_size个标签
    ind = y[idxs]
    # 计算grad_a与grad_b
    ga, gb = grad_func(inx, ind, a, b)
    # 求均值,除以batch_size
    sum_ga = np.sum(ga) / batch_size
    sum_gb = np.sum(gb) / batch_size
    # 完成更新
    a -= eta1 * sum_ga
    b -= eta1 * sum_gb
print (a)
print (b)
# 再次绘制直线观察结果
x_line1 = np.linspace(151000)
# 根据取的点套用公式得到直线
y_line1 = func(x_line1, a, b)
# 绘制数据散点图,用作对比用
plt.scatter(x, y, s=20, alpha=0.4, label="data")
# 绘制直线
plt.plot(x_line1, y_line1, lw=5, color="#990000", alpha=0.5, label="predict")
plt.legend()
plt.show()
5.190093956671917
-13.579019860865177

运行结果展示如下:

分类:

人工智能

标签:

人工智能

作者介绍

CV算法恩仇录
V1