本周通过书本和课程简单了解了关于机器学习的关键组件和各种机器学习问题,及深度学习的预备知识。
机器学习在生活中应用非常广泛,比如图像识别,语音转换文字,语言翻译等,极大的便利了我们的生活。机器学习本质上解决的是概率问题,通过大量数据的训练来对检测数据进行预测,而这中间的中介则是我们设计的模型,可以理解为从输入到输出的映射。
接下来介绍一些基本的知识点。
无论什么类型的机器学习问题,都会遇到这些组件:可以用来学习的数据、如何转换数据的模型、一个目标函数、调整模型参数以优化目标函数的算法。
首先是数据,数据是机器学习的基础,要想使模型达到良好的效果,数据不仅要量大而且要保证正确性,防止过拟合。
相关知识的联系为:
1.数据——样本(数据点或数据实例,独立分布)——称为特征或协变量(一组属性)——标签或目标(机器学习模型预测—特殊的的属性)
2.每个样本的特征类别数量相同——特征向量固定长度——维数
3.数据集——学习算法(使用数据集选择参数)——>参数(调整程序的行为)——操作参数形成的所有不同程序(输入—输出映射)的集合——>模型—模型族
4. 训练数据集(训练集):由一些为训练而收集的样本组成 在这个数据集上可以通过最小化总损失来学习模型参数的最佳值
测试数据集(测试集)
众所周知数据有很多种形式,比如图片,语音,文字等,但这些都不能直接被计算机所识别,所以这时候就需要有转换数据的模型,通过这个模型,接下来的工作才能如期展开。
被模型处理后的数据很多可以以张量的数据形式存储在计算机上,张量表示一个由数值组成的数组,这个数组可能有多个维度。 具有一个轴的张量对应数学上的向量; 具有两个轴的张量对应数学上的矩阵; 具有两个轴以上的张量没有特殊的数学名称。
目标函数是什么呢?有一个概念叫做参数,参数可以理解为旋钮,旋钮的转动影响模型的运行,在优化参数的过程中,会形成模型簇,不断形成更好的的模型,但又怎么评价模型的优劣呢?机器学习中的学习是指模型自主提高模型完成某些任务的效能,因此可以假设一个度量,而目标函数的作用就i是优化定义模型的优劣程度的度量,越低越好。又称为损失函数。
当任务在试图预测数值时,最常见的损失函数是平方误差(squared error),即预测值与实际值之差的平方;当试图解决分类问题时,最常见的目标函数是最小化错误率,即预测与实际情况不符的样本比例。这里可以看出对大葱的价格预测的损失函数很大几率会是平方误差。
有些目标函数(如平方误差)很容易被优化,有些目标(如错误率)由于不可微性或其他复杂性难以直接优化。 在这些情况下,通常会优化替代目标。
损失函数是根据模型参数定义的,并取决于数据集。
还有调整模型参数以优化目标函数的算法,大多流行的优化算法通常基于一种基本方法——梯度下降(在每个步骤中,梯度下降法都会检查每个参数,看看如果仅对该参数进行少量变动,训练集损失会朝哪个方向移动。 然后,它在可以减少损失的方向上优化参数——搜索出最佳参数,以最小化损失函数)。
机器学习可以解决很多问题,可以分为监督学习、无监督学习。
监督学习和无监督学习的最大区别在于数据中是否含有目标。
监督学习擅长在“给定输入特征”的情况下预测标签。 每个“特征-标签”对都称为一个样本。 有时,即使标签是未知的,样本也可以指代输入特征。 我们的目标是生成一个模型,能够将任何输入特征映射到标签(即预测)。
整个监督学习过程如下图所示:
监督学习的学习任务又包括回归、分类标记问题、搜索、推荐系统、序列学习,篇幅有限,这里不做详细介绍。我比较关注的是回归,判断回归问题的一个很好的经验法则是,任何有关“有多少”的问题很可能就是回归问题。所以我认为对大葱的价格预测解决的是回归问题。
无监督学习解决的问题更复杂,机器需要与环境进行互动,通过奖励的机制进行学习,这种学习被称为强化学习。
那无监督学习可以回答什么样的问题呢?比如聚类问题、主成分分析问题、因果关系和概率图模型问题、生成对抗性网络问题等。
深度学习与经典方法的区别主要在于:前者关注的功能强大的模型,这些模型由神经网络错综复杂的交织在一起,包含层层数据转换,因此被称为深度学习。浅层次的模型处理浅显的数据,深层次的模型处理较为抽象的数据。
从以上可以看出对数据处理的重要性。张量类更适合深度学习。以下以MXNET为例介绍对数据的处理。
首先创建一个张量对象
from mxnet import np, npx
npx.set_np()
x = np.arange(12) #创建一个从0到11的行向量
x.shape #访问张量(沿每个轴的长度)的形状
x.size #张量中元素的总数
X = x.reshape(3, 4) #将行向量转变为矩阵
#x.reshape(-1,4)
#x.reshape(3,-1)#-1自动计算出维度
np.zeros((2, 3, 4)) #将张量中的元素全部变为0
np.ones((2, 3, 4)) #将张量中的元素全部设置成1
np.random.normal(0, 1, size=(3, 4)) #每个元素都从均值为0、标准差为1的标准高斯分布(正态分布)中随机采样
np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]]) #最外层的列表对应于轴0,内层的列表对应于轴1
张量中的数学运算按元素运算
x = np.array([1, 2, 4, 8])
y = np.array([2, 2, 2, 2])
x + y, x - y, x * y, x / y, x ** y #**运算符是求幂运算
np.exp(x) #求幂
X = np.arange(12).reshape(3, 4)
Y = np.array([[2, 1, 4, 3], [1, 2, 3, 4], [4, 3, 2, 1]])
np.concatenate([X, Y], axis=0) #按行连接
np.concatenate([X, Y], axis=1) #按列连接
X == Y #通过逻辑运算符构建二元张量
X.sum() #张量中的所有元素进行求和,会产生一个单元素张量
广播机制:应对形状不相同的两个张量进行数学运算
-
通过适当复制元素来扩展一个或两个数组,以便在转换之后,两个张量具有相同的形状;
-
对生成的数组执行按元素操作
张量的访问:第一个元素的索引是0,最后一个元素索引是-1;可以用[1:3]
选择第二个和第三个元素(索引值为1和2)
X[1, 2] = 9 #赋值
X[0:2, :] = 12 #第一行到第二行都赋为12,因为放在前面
Z = np.zeros_like(Y)
Z[:] = X + Y #z原来指向的内存的内容被覆盖,以节约内存
x+=y也可以减少操作的内存开销
张量的转换
A = X.asnumpy()
B = np.array(A)
type(A), type(B) #NumPy张量转换为深度学习框架定义的张量
a = np.array([3.5])
a, a.item(), float(a), int(a) #大小为1的张量转换为Python标量
而要得到像上面提到的张量,是需要对数据进行预处理的
# !pip install pandas
import pandas as pd
import os
from mxnet import np
os.makedirs(os.path.join('..', 'data'), exist_ok=True)
data_file = os.path.join('..', 'data', 'house_tiny.csv')
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price\n') # 列名
f.write('NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,NA,106000\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n') #创建文件内容
data = pd.read_csv(data_file) #读入内存
inputs, outputs = data.iloc[:, 0:2], data.iloc[:, 2] #按列分为输入、输出
inputs = inputs.fillna(inputs.mean()) # 使用这一列的平均值填充缺失值
inputs = pd.get_dummies(inputs, dummy_na=True) 将一列字符转换为两列数字
X, y = np.array(inputs.values), np.array(outputs.values) #输入、输出转换为张量格式
x,y
对数据的处理还涉及数学知识,这当中包括线性代数,微积分,概率,这里我捡一些重要的东西说一说。
通过求和对张量进行降维:
x = np.arange(4)
x, x.sum()
默认情况下,调用求和函数会沿所有的轴降低张量的维度,使它变为一个标量。 我们还可以指定张量沿哪一个轴来通过求和降低维度。 以矩阵为例,为了通过求和所有行的元素来降维(轴0),可以在调用函数时指定axis=0
。 由于输入矩阵沿0轴降维以生成输出向量,因此输入轴0的维数在输出形状中消失。
A_sum_axis0 = A.sum(axis=0) #按行求和
A_sum_axis0, A_sum_axis0.shape
A.sum(axis=[0, 1]) #结果和A.sum()相同
A.mean(), A.sum() / A.size #求平均值
A.mean(axis=0), A.sum(axis=0) / A.shape[0] #按指定轴对向量进行降维
sum_A = A.sum(axis=1, keepdims=True) #非降维求和
np.dot(x, y) #点积
np.sum(x * y)
A.shape, x.shape, np.dot(A, x) #矩阵-向量积,张量形状不相等,先广播再按元素相乘
A=np.arrange(12).reshape(5,4)
B = np.ones(shape=(4, 3))
np.dot(A, B) #矩阵-矩阵乘积
u = np.array([3, -4])
np.linalg.norm(u) #求L2范数np.abs(u).sum() #求L1范数
微积分和概率的原理已经学过,不再赘述证明,这里注意一下自动微分
from mxnet import autograd, np, npx
npx.set_np()
x = np.arange(4.0)
x.attach_grad() #为梯度分配内存
x.grad #访问梯度
with autograd.record():
y = 2 * np.dot(x, x)
y.backward() #调用反向传播函数来自动计算y关于x每个分量的梯度
x.grad == 4 * x #验证
with autograd.record(): #计算另一个函数
y = x.sum()
y.backward()
x.grad #被新计算的梯度覆盖
当y
不是标量时,向量y
关于向量x
的导数的最自然解释是一个矩阵。 对于高阶和高维的y
和x
,求导的结果可以是一个高阶张量
# 当对向量值变量y(关于x的函数)调用backward时,将通过对y中的元素求和来创建
# 一个新的标量变量。然后计算这个标量变量相对于x的梯度
with autograd.record():
y = x * x # y是一个向量
y.backward()
x.grad # 等价于y=sum(x*x)
with autograd.record():
y = x * x
u = y.detach()
z = u * x
z.backward()
x.grad == u #分离计算
y.backward()
x.grad == 2 * x
使用自动微分的一个好处是: 即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度