黄佳 《零基础学机器学习》 chap2笔记
第2课 数学和Python基础知识
文章目录
- 黄佳 《零基础学机器学习》 chap2笔记
- 第2课 数学和Python基础知识
- 2.1 函数描述了事物间的关系
- 机器学习中常用的一些函数
- 2.2 捕捉函数的变化趋势
- 2.3 梯度下降
- 2.4 机器学习的数据结构--张量
- 2.4.1 张量的轴、阶和形状
- 2.4.2 标量--0D(阶)张量
- 2.4.3 向量--1D(阶)张量
- 2.4.4 矩阵--2D(阶)张量
- 2.4.5 序列数据--3D(阶)张量
- 2.4.6 图像数据--4D(阶)张量
- 2.4.7 视频数据--5D(阶)张量
- 2.4.8 数据的维度和空间的维度
- 2.5 Python的张量运算
- 2.5.1 机器学习中张量的创建
- 2.5.2 通过索引和切片访问张量中的数据
- 2.5.3 张量的整体操作和逐元素运算
- 2.5.4 张量的变形和转置
- 2.5.5 Python中的广播
- 2.5.6 向量和矩阵的点积运算
- 2.6 机器学习的几何意义
- 2.6.1 机器学习的向量空间
- 2.6.2 深度学习和数据流形
- 2.7 概率与统计研究了随机事件的规律
- 2.7.1 什么是概率
- 2.7.2 正态分布
- 2.7.3 方差和标准差
- 2.8 本章小结
- 2.9 习题
2.1 函数描述了事物间的关系
-
这里讲的不错哈哈👇
-
函数的输入和输出,很多情况下都是数字,但是也不完全如此。
-
函数可以反映非数字之间的关系。
- 比如,函数的输入可以是编号,输出可以是人名,关系就是“S11055607”→“黄先生”。在机器学习中,反映非数字之间的关系的函数就更常见了,比如,从狗的图片(输入)到狗的种类(输出)。
-
有以下两点需要注意。
- 输入集中的每一个元素X都要被“照顾”到(不过输出集并不一定需要完全覆盖。想象一下有一组狗的图片,全部鉴别完之后,发现其中缺少一个类型的狗,这是可能的)
- 函数的输出值是独一无二的。一个输入绝对不能够对应多个输出。比如,一张狗的图片,鉴定后贴标签时,认为既是哈士奇,又是德国牧羊犬。这种结果令人困惑,这样的函数我们也不接受。
-
-
机器学习中的函数
-
机器学习基本上等价于寻找函数的过程。机器学习的目的是进行预测、判断,实现某种功能。通过学习训练集中的数据,计算机得到一个从x到y的拟合结果,也就是函数。然后通过这个函数,计算机就能够从任意的x,推知任意的y。这里的自变量x,就是机器学习中数据集的特征,而特征的个数,通常会多于一个,记作x1,x2,…,xn,。如下图中的示例:机器学习通过电影的成本、演员等特征数据,推测这部电影可能收获的票房。
-
-
机器学习到的函数,实现了从特征到结果的一个特定推断。
机器学习到的函数模型有时过于复杂,并不总是能通过集合、解析式或者图像描述出来。- 然而,不能直观描述,并不等于函数就不存在了,机器学习所得到的函数正是事物之间的关系的体现,并发挥着预测功能。
- 换句话说,大数据时代的机器学习,不是注重特征到标签之间的因果逻辑,而是注重其间的相关关系。
- 那么如何衡量通过机器学习所得到的函数是不是好的函数呢?
- 在训练集和验证集上预测准确,而且能够泛化到测试集,就是好函数。
- 对结果判断的准确性,是机器学习函数的衡量标准,在这个前提之下,我们把科学体系中原本的核心问题“为什么”,转移到了“是什么”这个更加实用的目标。
-
可以说,机器学习算法得到的函数,往往能看到数据背后隐藏着的、肉眼所不能发现的秘密。
机器学习中常用的一些函数
传统的机器学习算法包括线性回归、逻辑回归、决策树、朴素贝叶斯等,通过应用这些算法可以得到不同的函数。
而深度学习的函数具有复杂的神经网络拓扑结构,网络中的参数通过链式求导来求得,相当于一大堆线性函数的跨层堆叠。它们仿佛存在于一片混沌之中,虽然看不见摸不着,却真实地存在着。
无论是传统的机器学习,还是深度学习,所得到的函数模型都是对样本集中特征到标签的关系的总结,是其相关性的一种函数化的表达。
-
线性函数
- 线性函数适合模拟简单的关系,比如,同一个小区房屋的面积和其售价之间可能会呈现线性的关系。
-
二次函数和多次函数
-
激活函数(activation function)
还有一组函数在机器学习中相当重要,它们是神经网络中的激活函数( activation function)。
如下图所示。它们的作用是在机器学习算法中实现非线性的、阶跃性质的变换。其中的Sigmoid函数在机器学习的逻辑回归模型中起着重要的作用。
-
对数函数
- 对数函数是指数函数(求幂)的逆运算。原来的指数就是对数的底。从几何意义上说,对数是将数轴进行强力的缩放,再大的数字经对数缩放都会变小。对数函数图像如下图所示。
2.2 捕捉函数的变化趋势
机器学习所关心的问题之一是捕捉函数的变化趋势,也就是研究y 如何随着x而变,这个趋势是通过求导和微分来实现的
2.3 梯度下降
-
对多元函数的各参数求偏导数, 然后把所求得的各个参数的偏导 数以向量的形式写出来, 就是梯度。
-
具体来说, 两个自变量的函数 f ( x 1 , x 2 ) f\left(x_{1}, x_{2}\right) f(x1,x2), 对应着机器学习数据 集中的两个特征, 如果分别对 x 1 , x 2 x_{1}, x_{2} x1,x2 求偏导数, 那么求得的梯度向量就 是 ( ∂ f / ∂ x 1 , ∂ f / ∂ x 2 ) T \left(\partial f / \partial x_{1}, \partial f / \partial x_{2}\right){ }^{\mathrm{T}} (∂f/∂x1,∂f/∂x2)T, 在数学上可以表示成 Δ f ( x 1 , x 2 ) \Delta f\left(x_{1}, x_{2}\right) Δf(x1,x2) 。
-
那么计算梯度向量的意义何在呢? 其几何意义, 就是函数变化的 方向, 而且是变化最快的方向。
-
对于函数 f(x), 在点 ( x 0 , y 0 ) \left(x_{0}, y_{0}\right) (x0,y0), 梯 度向量的方向也就是 y 值增加最快的方向。也就是说, 沿着梯度向量的方向 Δ f ( x 0 ) \Delta f\left(x_{0}\right) Δf(x0), 能找到函数的最大值。反过来说, 沿着梯度向量相反的方向, 也就是
− Δ f ( x 0 ) -\Delta f\left(x_{0}\right) −Δf(x0) 的方向, 梯度减少最快, 能找到函数的最小值。
如果某一个点的梯度向量的值为 0 , 那么也就是来到了导数为 0 的函数最低点(或局部最低点)了。
为什么不同教材中凸函数和凹函数的定义是不同的? - 秦浩然的回答 - 知乎 https://www.zhihu.com/question/31160556/answer/51276335
2.4 机器学习的数据结构–张量
- 在机器学习中,把用于存储数据的结构叫作张量(tensor) ,矩阵是二维数组,机器学习中就叫作2D张量
2.4.1 张量的轴、阶和形状
-
张量是机器学习程序中的数字容器,本质上就是各种不同维度的数组,如下图所示。我们把张量的维度称为轴( axis)(就是数学中的x轴,y轴,…….),轴的个数称为阶(rank)(也就是俗称的维度,但是为了把张量的维度和每个阶的具体维度区分开,这里统一把张量的维度称为张量的阶。NumPy中把它叫作数组的轶)
-
张量的形状(shape)就是张量的阶,加上每个阶的维度(每个阶的元素数目)。
-
张量都可以通过NumPy来定义、操作。因此,把NumPy数学函数库里面的数组用好,就可以搞定机器学习里面的数据结构。
2.4.2 标量–0D(阶)张量
-
我们从最简单的数据结构开始介绍。仅包含一个数字的张量叫作标量(scalar),即0阶张量或0D张量。 标量的功能主要在于程序流程控制、设置参数值等。
-
下面创建一个NumPy标量
import numpy as np #导入NumPy X = np.array(5) # 创建0D张量,也就是标量 print("X的值",X) print("X的阶",X.ndim) #ndim属性显示张量轴的个数 print("X的数据类型",X.dtype) # dtype属性显示张量数据类型 print("X的形状",X.shape) # shape属性显示张量形状
2.4.3 向量–1D(阶)张量
由一组数字组成的数组叫作向量(vector),也就是一阶张量,或称1D张量。一阶张量只有一个轴。
-
下面创建一个NumPy向量
X = np.array([5,6,7,8,9]) #创建1D张量,也就是向量 print("X的值",X) print("X的阶",X.ndim) #ndim属性显示张量轴的个数 print("X的形状",X.shape) # shape属性显示张量形状
-
创建向量的时候要把数字元素放进方括号里面,形成一个包含5个 元素的1D张量。需要再次强调的是,机器学习中把5个元素的向量称 为5维向量。千万不要把5维向量和5阶张量混淆
- 上面这里是 ndim=1,1个轴,1阶张量, 有5个元素, 在这一阶有五个元素,是五维
-
注意点1
-
向量的维度
- 这的确是机器学习过程中比较容易让人感到混乱的地方。其原因在于:维度((dimensionality)(也就是英文字母D)可以表示沿着某个轴上的元素个数(如5D向量),也可以表示张量中轴的个数(如5D张量),这的确会令人感到混乱。还是那句话,为了区别两者,把5D张量称为5阶张量,而不称为5维张量。
-
注意点2
-
再看一下X向量的形状(5,)。这个描述方式也是让初学者比较困惑的地方,如果没有后面的逗号,可能看起来更舒服一点儿。但是我们要习惯,(5,)就表示它是一个1D张量,元素数量是5,也就是5维向量。
-
下面这个语句又创建了一个向量,这个向量是一个1维向量:
X= np.array([5])#1维向量,也就是1D数组里面只有一个元素
- 这个语句和刚才创建标量的语句“X = np.array (5)”的唯一区别只是数字5被方括号括住了。正是因为这个方括号,这个语句创建出来的就不是数字标量,而是一个向量,即1D张量。它的轴的个数是1,形状是(1,) ,而不是()。
-
-
-
机器学习中的向量数据
-
结合波士顿房价数据集来理解
from keras.datasets import boston_housing # 波士顿房价数据集 (X_train, y_train), (X_test, y_test) = boston_housing.load_data() print("X_train的形状:", X_train.shape) print("X_train中第一个样本的形状:", X_train[0].shape) print("y_train的形状:", y_train.shape)
-
这个是Keras内置的波士顿房价数据集,是一个2D的普通数值数据集。
- X_train是一个2D矩阵,是404个样本数据的集合。而y_train的形状,正是一个典型的向量,它是一个404维的标签向量。其实几乎所有的标签集的形状都是向量。
-
-
X_train[0]又是什么意思呢?它是X_train训练集的第一行数据, 这一行数据,是一个13维向量(也是1D张量)。也就是说,训练集的每行数据都包含13个特征。
-
-
向量的点积
-
两个向量之间可以进行乘法运算,而且不止一种,有点积( dotproduct)(也叫点乘)和叉积 ( cross product) (也叫叉乘),其运算法则不同。机器学习中经常出现点积运算。
[ a 1 a 2 ⋅ ⋅ a n − 1 a n ] ⋅ [ b 1 b 2 ⋅ ⋅ b n − 1 b n ] = a 1 b 1 + a 2 b 2 + ⋯ + a n − 1 b n − 1 + a n b n 向 量 的 点 积 运 算 法 则 \left[\begin{array}{c}\\a_{1} \\\\a_{2} \\\\\cdot \\\\\cdot \\\\a_{n-1} \\\\a_{n}\\\end{array}\right] \cdot\left[\begin{array}{c}\\b_{1} \\\\b_{2} \\\\\cdot \\\\\cdot \\\\b_{n-1} \\\\b_{n}\\\end{array}\right]=a_{1} b_{1}+a_{2} b_{2}+\cdots+a_{n-1} b_{n-1}+a_{n} b_{n}\\\\向量的点积运算法则\\ ⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡a1a2⋅⋅an−1an⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤⋅⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡b1b2⋅⋅bn−1bn⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤=a1b1+a2b2+⋯+an−1bn−1+anbn向量的点积运算法则
简单地说, 就是两个相同维度的向量对应元素先相乘, 后相加, 形成等号右边的多项式-
python实现向量点积
weight = np.array([1,-1.8,1,1,2]) # 权重向量(也就是多项式的参数) X = np.array([1,6,7,8,9]) # 特征向量(也就是一个特定样本中的特征值) y_hat = np.dot(X,weight) # 通过点积运算构建预测函数 print('函数返回结果:', y_hat) # 输出预测结果
或者下面的也可以👇
y_hat = weight.dot(X) # X.dot(weight)也可以实现同样结果 y_hat
注意向量点积的结果是一个值,也就是一个标量,而不是一个向量。
通过向量、矩阵等数据结构进行向量化运算是机器学习中的一个关键技术。而Python能够方便地实现向量化运算,正是Python核心优势之一。在上面两段代码中,点积运算就是通过向量化运算直接实现的,过程中没有出现任何for循环语句。
另外,在向量的点积运算中,A·B=B·A,向量可以互换位置。不过,下面要介绍的矩阵间的点积,或者矩阵和向量之间的点积,就没有这么随意了。
-
2.4.4 矩阵–2D(阶)张量
-
矩阵(matrix)是一组一组向量的集合。矩阵中的各元素横着、竖着、斜着都能构成不同的向量。而矩阵,也就是 2 阶张量,或称 2D 张量,其形状为(m,n)。
-
矩阵里面横向的元素组称为“行”,纵向的元素组称为“列”。 一个矩阵从左上角数起的第 i i i行第 j j j列上的元素称为第 ( i , j ) (i,j) (i,j)项,通常记为 a ( i , j ) a_{(i,j)} a(i,j)。
-
机器学习中的矩阵数据
-
机器学习中的矩阵数据比比皆是,因为普通的向量数据集都是读入矩阵后进行处理。
-
矩阵是2D张量,形状为 (样本,特征)。第一个轴是样本轴, 第二个轴是特征轴。
-
看一看刚才载入的波士顿房价数据集的特征矩阵,这个矩阵的形状是(404,13),也就是404个样本,13个特征:
print("X_train的内容:", X_train) #X_train是2D张量,即矩阵
- 除房价数据集外,再举两个其他类似数据集的例子。
- 公司客户数据集,用于分析客户,包括客户的姓名、年龄、银行账户、消费数据等4个特征,共10000个客户。此数据集形成的张量形状为(10 000,4) 。
- 城市交通数据集,用于研究交通状态,包括城市的街道名、经度、维度、交通事故数量等28个交通数据特征,共800个街道。此数据集形成的张量形状为( 800,28) 。
- 这些数据集读入机器之后,都将以2D张量,也就是矩阵的格式进行存储。
-
-
矩阵的点积
-
矩阵之间也可以进行点积。具体来说,是第一个矩阵的行向量和第二个矩阵的列向量进行点积,然后把结果标量放进新矩阵,作为结果矩阵中的一个元素。这个规则如下图所示。
注意,当两个矩阵相乘时,第一个矩阵的列数必须等于第二个矩阵的行数。即形状为(m,n)的矩阵乘以形状为(n,m)的矩阵
结果得到一个矩阵(m,m)。也就是说,如果一个矩阵A的形状是(1,8) ,一个矩阵B的形状是(3,2),那么它们之间就无法进行点积运算。
- 一个可行的解决方案是,将A矩阵变形为4x2矩阵,并将B变形为2x3矩阵,而后进行点积,就得到一个形状为(4,3)的4x3矩阵。在Python中可以用reshape方法对矩阵进行变形操作。
- 和向量的点积一样,矩阵的点积也是通过NumPy的dot方法实现,**这节省了很多的for循环语句**。否则,矩阵的运算是需要循环嵌套循环才能实现的。
2.4.5 序列数据–3D(阶)张量
- 例
# 创建3D张量 X = np.array([[[1, 22, 4, 78, 2], [2, 59, 6, 56, 1], [3, 31, 8, 54, 0]], [[4, 56, 9, 34, 1], [5, 78, 8, 35, 2], [6, 34, 7, 36, 0]], [[7, 45,5, 34, 5], [8, 53, 6, 35, 4], [9, 81, 4, 36, 5]]])
那么序列数据多出来哪个轴呢?就是序列的步长。对于时间序列数据来说,就是时戳(timestamp),也叫时间步
- 例:天气状况
-
因为增加了时戳,所以表里面的行列结构显得更为复杂。读取入机器进行处理时,需要把行里面的时间步拆分出来。
-
第一个轴—样本轴,一年记录下来的数据共365个,也就是365维。
-
第二个轴—时间步轴,每天一共是24小时,每小时4个15分钟共96维。
-
第三个轴─特征轴,一共是温度、湿度、风力3个维度。因此,这个数据集读入机器之后的张量形状是(365,96,3) 。
-
-
文字序列数据集
- 类似地,还有文字序列数据集。假设有一些客户的评论数据,每条评论编码成100个字组成的序列,而每个字来自1000个汉字的简易字典。在这种情况下,每个字符可以被编码为1000字节的二进制向量(只有在该字符对应的索引位置,值为1,其他二进制位值都为0,这种编码就是one-hot编码)。那么每条评论就被编码为一个形状为(100,1 000)的2D张量。如果收集了10 000条的客户评论,这个客户评论数据集就可以存储在一个形状为(10 000,100,1 000)的张量中,供机器去学习。
此时,文字序列数据集的形状为3D张量:(样本,序号,字编码)
- 类似地,还有文字序列数据集。假设有一些客户的评论数据,每条评论编码成100个字组成的序列,而每个字来自1000个汉字的简易字典。在这种情况下,每个字符可以被编码为1000字节的二进制向量(只有在该字符对应的索引位置,值为1,其他二进制位值都为0,这种编码就是one-hot编码)。那么每条评论就被编码为一个形状为(100,1 000)的2D张量。如果收集了10 000条的客户评论,这个客户评论数据集就可以存储在一个形状为(10 000,100,1 000)的张量中,供机器去学习。
2.4.6 图像数据–4D(阶)张量
-
图像数据本身包含高度、宽度,再加上一个颜色深度通道。MNIST数据集中是灰度图像,只有一个颜色深度通道;而GRB格式的彩色图像,颜色深度通道的维度为3。
-
因此,对于图像数据集来说,长、宽、深再加上数据集大小这个维度,就形成了4D张量(如下图所示),其形状为(样本,图像高度,图像宽度,颜色深度),如MNIST特征数据集的形状为(60000,28,28,1)
-
在机器学习中,不是对上万个数据样本同时进行处理,那样的话机器也受不了,而是一批一批地并行处理,比如指定批量大小为64。此时每批的100px ×100px的彩色图像张量形状为(64,100,100 ,3),如果是灰度图像,则为(64,100,100,1) 。
2.4.7 视频数据–5D(阶)张量
-
机器学习的初学者很少有机会见到比4D更高阶的张量。如果有,视频数据的结构是其中的一种。
-
视频可以看作是由一帧一帧的彩色图像组成的数据集。
-
每一帧都保存在一个形状为(高度,宽度,颜色深度)的3D张量中。
-
一系列帧则保存在一个形状为(帧,高度,宽度,颜色深度)的4D张量中。
-
-
因此,视频数据集需要5D张量才放得下,其形状为**(样本,帧,高度,宽度,颜色深度)**。
-
可以想象,视频数据的数据量是非常大的(例如,一个10分钟的普通视频,每秒采样3~4帧,这个视频转换成机器能处理的张量后,可能包含上亿的数据量)。面对这种规模的数据,普通的机器学习模型会感到手足无措,只有深度学习模型才能够搞定。
-
2.4.8 数据的维度和空间的维度
-
数据的维度
-
前面说过,“维度”这个概念有时会造成一些混淆。因为我们会听到,一维数组、二维数组、三维数组之类的话。而在机器学习中又时常听说数据集中的特征,是一个向量,可能是一维、二维、三维、一百维甚至一万维的向量。
- 迷惑来了—向量不应该都是一维( 1D)的数组吗?怎么又说是百维、一万维的向量?好奇怪!到底是多少维?
-
其实,在机器学习中,维度指的是在一个数据轴上的许多点,也就是样本的个数(样本轴上点的个数)或者特征的个数(特征轴上点的个数)。一万个不同的特征,就是一万维;而一万个数据样本,也同样可称为一万维。
- 为了标准化叙述,我们把张量的每一个数据轴,统称为阶。因此,我们说一阶(1D)向量、二阶(2D)矩阵、三阶(3D)张量,而不是说维。
- 但事实上,很多机器学习的教程也没有实现这种统一的标准化叙述。因此,在外面听到一维向量、二维矩阵、三维数组这样的叫法也毫不奇怪,而且这些叫法也没有错,只是容易让人混淆而已
-
空间的维度
-
这本书铺垫的真的不错哈哈哈
-
还有一点需要注意,在实际项目中,特征(也就是自变量x)的个数,都是很多的。然而在画图说明的时候,大多以一个特征x或两个特征x1、x2为例来表现x和y的关系,很少画出超过两个特征维度的情况,这是为什么呢?
-
因为仅有一个特征的数据集,关系很容易被展示,从房屋面积到房价,很直接,x轴特征,y轴标签。此时一个特征维,加上一个标签维,就是二维图形,在纸面上显示没有难度。
-
如果有两个特征,x1代表房屋面积,x2代表楼层,这两个特征和房价y之间的函数,怎么展示?那么可以画出一个有深度的平面显示x、x坐标,立体显示y值。这是从二维的平面上显示三维图形,已经需要一些透视法的作图技巧。
-
-
对于分类问题,也可以x1作为一个轴,x2作为一个轴,用圈、点、叉,或者不同颜色的点显示y的不同分类值。这是另一种用平面显示三维信息的方法。
-
那么特征再多一维呢?很难展示。比如,凸函数,一维特征的凸函数,是一条曲线,而二维特征的凸函数,就像一个碗。三维特征的凸函数是什么样的呢 ? 我们不知道。如果非要描绘x1、x2、x3与y的关系,就需要先应用降维(dimensionality reduction)算法处理数据,把维度降到二维以内。
-
这个局限来自空间本身只有3个维度,长、宽、深。绘图的时候,如果特征有两维,再加一维标签y,就把三维空间占全了。因此,我们既无法想象,也无法描绘更多维的函数形状。
-
2.5 Python的张量运算
如何操作张量
2.5.1 机器学习中张量的创建
-
我们知道,机器学习中的张量大多是通过NumPy数组来实现的。 NumPy数组和Python的内置数据类型列表不同。列表的元素在系统内存 中是分散存储的,通过每个元素的指针单独访问,而 NumPy 数组内 各元素则连续的存储在同一个内存块中,方便元素的遍历,并可利用现代CPU的向量化计算进行整体并行操作,提升效率。因此NumPy 数 组要求元素都具有相同的数据类型,而列表中各元素的类型则可以不同。
-
将列表、元组转化为np数组
import numpy as np # 导入NumPy数学工具集 lis=[1, 2, 3, 4, 5] # 创建列表 array_01=np.array([1,2,3,4,5]) # 列表转化成数组 array_02=np.array((6,7,8,9,10)) # 元组转化成数组 array_03=np.array([[1,2,3],[4,5,6]]) # 列表转化成2D数组 print ('列表:', lis) print ('列表转化为数组:', array_01) print ('元组转化为数组:', array_02) print ('2D数组:', array_03) print ('数组的形状:', array_01.shape) # print ('列表的形状:', list.shape) # 列表没有形状,程序会报错
-
利用np的方法直接创建数组
https://blog.csdn.net/neweastsun/article/details/99676029
-
array_04=np.arange(1,5,1) # 通过arange函数生成数组 array_05=np.linspace(1,5,5) # 通过linspace函数生成数组 print (array_04) print (array_05)
-
arange(a,b,c)函数产生a~b(不包括b),间隔为c的一个数 组;
-
而linspace(a,b,c)函数是把a~b(包括b),产生c个观测值。
-
当然,机器学习的数据集并不是在程序里面创建的,大多是先从文本文件中把所有样本读取至Dataframe格式的数据,然后用array方法 或其他方法把Dataframe格式的数据转换为NumPy数组,也就是张量, 再进行后续操作。
2.5.2 通过索引和切片访问张量中的数据
-
可以通过**索引(indexing)和切片(slicing)**这两种方式访问张量, 也就是NumPy数组元素。索引,就是访问整个数据集张量里面的某个具体数据;切片,就是访问一个范围内的数据。
-
对一阶张量的切片
array_06 = np.arange(10) print (array_06) index_01 = array_06[3] # 索引-第4个元素 print ('第4个元素', index_01) index_02 = array_06[-1] # 索引-最后一个元素 print ('第-1个元素', index_02) slice_01 = array_06[:4] # 从0到4切片 print ('从0到4切片', slice_01) slice_02 = array_06[0:12:4] # 从0到12切片,步长为4 print ('从0到12切片,步长为4', slice_02)
-
数组无论是生成的时候,还是访问的时候,都是从0开始的。索引3,就是第4个元素
-
负号,表示针对当前轴终点的相对位置,因此这里**-1指的就是倒数第一个元素**。冒号,是指区间内的所有元素,如果没限定区间,就代表轴上面的所有元素。
-
对多阶张量的切片
-
将不同轴上的切片操作用逗号隔开
-
例如,对MNIST数据集中间的 5000个数据样本进行切片:
from keras.datasets import mnist #需要打开internet选项 (X_train, y_train), (X_test, y_test) = mnist.load_data() print (X_train.shape) X_train_slice = X_train[10000:15000,:,:]
10000:15000,就是把样本轴进行了切片。而后面两个冒号的意思是,剩下的两个轴里面的数据,全都保留(对这个图片样本集,如果 后面两个轴也切片,图片的28px×28px的结构就被破坏了,相当于把图片进行了裁剪)。
-
-
数组访问示例
array_07 = np.array([[1,2,3],[4,5,6]]) print (array_07[1:2],'它的形状是', array_07[1:2].shape) print (array_07[1:2][0], '它的形状又不同了', array_07[1:2][0].shape)
[[4 5 6]] 它的形状是 (1, 3) [4 5 6] 它的形状又不同了 (3,)
- 同 样都是4、5、6这3个数字形成的张量,[[4 5 6]]被两个方括号括 起来,[4 5 6]被一个方括号括起来,两者阶的数目就不一样。还是那句话,张量是机器学习的数据结构,其形状是数据处理的关键,这是不能马虎的。
2.5.3 张量的整体操作和逐元素运算
-
张量的算术运算,包括加、减、乘、除、乘方等,既可以整体进行,也可以逐元素进行。
-
例如,下面的语句就是对张量的所有元素进行整体操作:
array_07 += 1 # 数组内全部元素加1 print (array_07)
- 等价于通过循环嵌套实现的逐元素操作:
for i in range(array_07.shape[0]): for j in range(array_07.shape[1]): array_07[i,j] += 1
2.5.4 张量的变形和转置
-
张量变形(reshaping)也是机器学习中的一个常见操作,可以通过 NumPy中的reshape方法实现。
-
什么是变形?怎么变形?很简单。一个形状为(2,3)的矩阵,可以变形为(3,2)的矩阵。元素还是那些元素,但是形状变了。
-
例
print (array_07,'形状是', array_07.shape) print (array_07.reshape(3,2), '形状是', array_07.reshape(3,2).shape)
-
可以调用方法直接转置(transpose)
array_07 = np.array([[1,2,3],[4,5,6]]) array_07 = array_07.T # 矩阵的转置 print (array_07, ' 矩阵转置后形状是', array_07.shape)
-
例
array_06 = np.arange(10) print (array_06,'形状是', array_06.shape,'阶为', array_06.ndim) array_06 = array_06.reshape(10,1) print (array_06,'形状是', array_06.shape,'阶为', array_06.ndim)
- 尽管从数据集本身来说,仍然是0~9这10个数字,但变形前后的张量形状和阶数有很大差别。这个从1阶到2阶的张量变形过程,在下一 课线性回归中还会见到。
2.5.5 Python中的广播
机器学习领域有这样一种说法,如果使用很多的for循环语句,那么说明此人还未了解机器学习的精髓。
看看前面我们完成的两个项目。一个波士顿房价预测,一个MNIST图片识别,这两个数据集,里面都包含成百 上千乃至上万个数据样本。你们看见任何一行for语句代码了吗?
处理如此大的数据集而不需要循环语句,用传统的编程思维理解起来是不是很离奇呢?
-
下面说明一下。 首先利用了Python对于数组,也就是张量整体地并行操作。很大、 很高阶的数据集,读入NumPy数组之后,并不需要循环嵌套来处理, 而是作为一个整体,直接加减乘除、赋值、访问。这种操作很好地利 用了现代CPU以及GPU/TPU的并行计算功能,效率提升不少。
-
另外一个技巧,就是Python的广播(broadcasting)功能。这是 NumPy对形状不完全相同的数组间进行数值计算的方式,可以自动自发地把一个数变成一排的向量,把一个低维的数组变成高维的数组。
-
举例来说,你们看数组的算术运算通常在相应的元素上进行。这 要求两个数组a和b形状相同,也就是 a.shape = b.shape,那么a+b的结 果就是a与b数组对应位相加。这要求张量的阶相同,且每个轴上的维度(长度)也相同。减、乘(不是指点乘)、除等算术运算,也都是 如此。
-
广播,就是跟着对应阶中维度较大,也就是较为复杂的张量进行填充。用图展示就更为清楚了,如下图所示。图中a的形状是(4, 3),是二阶张量,b的形状是(1,3),也是二阶张量,那么结果就 是把张量b的行进行复制,拉伸成一个形状为(4,3)的张量,然后再与张量a相加。
-
示例代码
array_08 = np.array([[0,0,0], [10,10,10], [20,20,20], [30,30,30]]) array_09 = np.array([[0,1,2]]) array_10 = np.array([[0],[1],[2],[3]]) list_11 = [[0,1,2]] print ('array_08的形状:', array_08.shape) print ('array_09的形状:', array_09.shape) print ('array_10的形状:', array_10.shape) array_12 = array_09.reshape(3) print ('array_12的形状:', array_12.shape) array_13 = np.array([1]) print ('array_13的形状:', array_13.shape) array_14 = array_13.reshape(1,1) print ('array_14的形状:', array_14.shape) print ('08 + 09结果:',array_08 + array_09) print ('08 + 10结果:',array_08 + array_10) print ('08 + 11结果:',array_08 + list_11) print ('08 + 12结果:',array_08 + array_12) print ('08 + 13结果:',array_08 + array_13) print ('08 + 14结果:',array_08 + array_14)
array_08的形状: (4, 3) array_09的形状: (1, 3) array_10的形状: (4, 1) array_12的形状: (3,) array_13的形状: (1,) array_14的形状: (1, 1) 08 + 09结果: [[ 0 1 2] [10 11 12] [20 21 22] [30 31 32]] 08 + 10结果: [[ 0 0 0] [11 11 11] [22 22 22] [33 33 33]] 08 + 11结果: [[ 0 1 2] [10 11 12] [20 21 22] [30 31 32]] 08 + 12结果: [[ 0 1 2] [10 11 12] [20 21 22] [30 31 32]] 08 + 13结果: [[ 1 1 1] [11 11 11] [21 21 21] [31 31 31]] 08 + 14结果: [[ 1 1 1] [11 11 11] [21 21 21] [31 31 31]]
-
广播规则总结
-
对两个数组, 从后向前比较它们的每一个阶(若其中一个数组没有 当前阶则忽略此阶的运算)
-
对于每一个阶, 检查是否满足下列条件:
-
if当前阶的维度相等 then可以直接进行算术操作; else if当前阶的维度不相等, 但其中一个的值是1 then通过广播将值为1的维度进行“复制”(也形象地称为 “拉伸”)后, 进行算术操作; else if, 上述条件都不满足, 那么两个数组当前阶不兼容, 不能 够进行广播操作 then 抛 出 "Value Error: operands could not be broadcast together" 异常;
-
2.5.6 向量和矩阵的点积运算
-
向量的点积运算
- 对于向量 a 和向量 b :
a = [ a 1 , a 2 , ⋯ , a n ] b = [ b 1 , b 2 , ⋯ , b n ] 其 点 积 运 算 规 则 如 下 : a ⋅ b = a 1 b 1 + a 2 b 2 + ⋯ + a n b n \\\\\begin{array}{l}\\a=\left[a_{1}, a_{2}, \cdots, a_{n}\right] \\\\b=\left[b_{1}, b_{2}, \cdots, b_{n}\right] \\\\\end{array}\\\\其点积运算规则如下:\\\\a \cdot b=a_{1} b_{1}+a_{2} b_{2}+\cdots+a_{n} b_{n} a=[a1,a2,⋯,an]b=[b1,b2,⋯,bn]其点积运算规则如下:a⋅b=a1b1+a2b2+⋯+anbn
-
这个过程中要求向量a和向量b的维度相同。向量点积的结果是一 个标量,也就是一个数值。
-
点积代码示例
-
vector_01 = np.array([1,2,3]) vector_02 = np.array([[1],[2],[3]]) vector_03 = np.array([2]) vector_04 = vector_02.reshape(1,3) print ('vector_01的形状:', vector_01.shape) print ('vector_02的形状:', vector_02.shape) print ('vector_03的形状:', vector_03.shape) print ('vector_04的形状:', vector_04.shape) print ('01和01的点积:', np.dot(vector_01,vector_01)) print ('01和02的点积:', np.dot(vector_01,vector_02)) print ('04和02的点积:', np.dot(vector_04,vector_02)) print ('01和数字的点积:', np.dot(vector_01,2)) print ('02和03的点积:', np.dot(vector_02,vector_03)) print ('02和04的点积:', np.dot(vector_02,vector_04)) # print ('01和03的点积:', np.dot(vector_01,vector_03)) # 程序会报错 # print ('02和02的点积:', np.dot(vector_02,vector_02))
-
vector_01的形状: (3,) vector_02的形状: (3, 1) vector_03的形状: (1,) vector_04的形状: (1, 3) 01和01的点积: 14 01和02的点积: [14] 04和02的点积: [[14]] 01和数字的点积: [2 4 6] 02和03的点积: [2 4 6] 02和04的点积: [[1 2 3] [2 4 6] [3 6 9]]
- 输出结果中有以下细节要注意。
- 前3个输出,结果虽然都是一个值,但是形状不同,第1个是标量,第2个是形状为1D张量的值,第3个是形状为2D张量的值。
- 而后面3个输出,都不再是一个值,而是向量或矩阵,遵循的是矩阵点积的规则。
- 最后两个点积,01和03,以及02和02,由于不满足张量之间点积的规则,系统会报错。
- 张量的各种形状的确让人眼花缭乱,因此才更要不时地查看,以确保得到的是所要的数据结构。
-
-
矩阵的点积运算
-
matrix_01 = np.arange(0,6).reshape(2,3) matrix_02 = np.arange(0,6).reshape(3,2) print(matrix_01) print(matrix_02) print ('01和02的点积:', np.dot(matrix_01,matrix_02)) print ('02和01的点积:', np.dot(matrix_02,matrix_01)) print ('01和01的点积:', np.dot(matrix_01,matrix_01))
-
矩阵的点积操作,常常出现在神经网络的权重计算中
-
2.6 机器学习的几何意义
- 如何用几何(形状、大小、图形的相对位置等空间区域)的方式去表述机器学习的本质呢?做一些尝试
2.6.1 机器学习的向量空间
张量,可以被解释为某种几何空间内点的坐标。这样,机器学习中特征向量就形成了特征空间,这个空间的维度和特征向量的维度相同。
现在考虑这样一个二维向量:A = (0.5,1)
这个向量可以看作二维空间中的一个点,一般将它描绘成原点到这个点的箭头,如上图所示。那么更高维的向量呢?应该也可以想象为更高维空间的点。像这样把平面数字转换为空间坐标的思考方式其实是很有难度的。
张量运算都有几何意义。举个例子,我们来看二维向量的加法,如下图所示。向量的加法在几何上体现为一个封闭的图形。两个向量的和形成一个平行四边形,结果向量就是起点到终点的对角线。
这些例子展示了平面中一些二维向量操作的几何意义,推而广之:**机器学习模型是在更高维度的几何空间中对特征向量进行操作、 变形,计算其间的距离,并寻找从特征向量到标签之间的函数拟合,**这就是从几何角度所阐述的机器学习本质。
几种常见的机器学习模型都可以通过特征空间进行几何描述,如下图所示
2.6.2 深度学习和数据流形
-
深度学习的几何意义。
-
深度学习的过程,实际上也就是一个数据提纯的过程。数据从比较粗放的格式,到逐渐变得“计算机友好”。
-
数据为什么需要提纯呢?主要还是因为特征维度过高,导致特征空间十分复杂,进而导致机器学习建模过程难度过大。有一种思路是通过流形(manifold)学习将高维特征空间中的样本分布群“平铺”至一个低维空间,同时能保存原高维空间中样本点之间的局部位置相关信息。
-
原始数据特征空间中的样本分布可能极其扭曲,平铺之后将更有利于样本之间的距离度量,其距离将能更好地反映两个样本之间的相似性。原始空间中相邻较近的点可能不是同一类点,而相邻较远的点有可能是同一类,“平铺”至低维空间后就能解决这一问题。
-
在传统的机器学习中,流形学习主要用于特征提取和数据降维,特征提取使特征变得更加友好,降维是因为高维数据通常有冗余。而在深度学习出现之后,有一种说法认为神经网络能够自动自发地将复杂的特征数据流形展开,从而减少了特征提取的需要。从直观上,这个展开过程可以用一团揉皱了的纸来解释,如下图所示。
- 如果有好几张揉皱了的纸上写满了数字,要读取上面的信息是不可能的事。但把这样的纸展开,而又不损害纸,也挺麻烦。因此,现代的深度神经网络(Deep Neural Networks,DNN)通过参数学习,展开了高维数据的流形—这可以说是深度学习的几何意义
-
2.7 概率与统计研究了随机事件的规律
2.7.1 什么是概率
- 概率的定义和计算公式
表中公式都不难理解,最后一个公式P(A| B)的意义叫作条件概率,也叫后验概率。也就是说已知事件B发生的时候,A的概率。
-
例题
-
举个例子来解释:某公司男生、女生各占50%,烟民占总人数的10%,而女烟民则占总人数的1%。那么问题来了:现在遇到了一个烟民,这个烟民是女生的可能性有多大?
现在是已知3个概率后,能够算出来第4个概率,根据条件概率公式进行推导计算,需要注意以下几个地方。- 事件B一烟民。
- 事件A—女生。
- P(B)——10%,随便遇到一个烟民的概率。P(A)—50%,随便遇到一个女生的概率。
- P(BA)—1%,已知100个人里面才有一个女烟民。
- P(A|B)一现在遇到一个烟民(事件B发生了),是女生的可能性有多大?
2.7.2 正态分布
- 正态分布(normal distribution)这个词你们肯定听起来很耳熟,但是也许不知道它的确切定义。其实,所谓分布就是一组概率的集合是把一种常见的概率分布用连续的函数曲线显示出来的方式。而正态分布,又名高斯分布(Gaussian distribution),则是一个非常常见的连续概率分布。
- 正态分布也叫概率分布的钟形曲线( bell curve) ,因为曲线的形状就像一口悬挂的大钟,如下图所示。
2.7.3 方差和标准差
- 上面正态分布示意图中出现了一个奇怪的符号 σ \sigma σ,这个符号代表标准差( Standard Deviation ,SD),读作sigma。标准差,也称均方差( mean square crror)),是反映研究总体内个体之间差异程度的一种统计指标。
- 标准差是根据方差计算出来的。而方差( variance)是一组资料中各实际数值与其算术平均数(即均值(mean) ,也叫期望值)的差值做平方结果相加之后,再除以总数而得。标准差是方差的算术平方根。方差和标准差,描述的都是数据相对于其期望值的离散程度。
- 标准差在机器学习中也经常出现,比如,当进行数据预处理时,常常要涉及数据标准化。在该步骤中,最常见的做法就是对样本特征减去其均值,然后除以其标准差来进行缩放。
2.8 本章小结
-
我们介绍函数的定义并给出几种类型的函数图像,从直观上去理解机器学习如何通过函数对特征和标签之间的关联性进行拟合。然后介绍的对函数进行求导、微分以及梯度下降方法则是机器学习进行参数优化的最基本原理。具体的细节在下一课中介绍。
-
机器学习中的数据结构称为张量,下面是几种重要的张量格 式,用于处理不同类型的数据集。
- 普通向量数据集结构:2D张量,形状为(样本,标签)。
- 时间序列数据集或序列数据集:3D张量,形状为(样本,时 戳,特征)。
- 图像数据集:4D张量,形状为(样本,图像高度,图像宽度, 颜色深度)。
-
Python语句操作方面,NumPy数组的操作都是重点内容。
- 张量的切片操作。
- 用reshape进行张量变形。
- Python的广播功能。
- 向量和矩阵的点积操作之异同,向量的点积得到的是一个数。
- 要记得不定时地检查张量的维度。
2.9 习题
import numpy as np # 导入NumPy数学工具箱
import pandas as pd # 导入Pandas数据处理工具箱
from keras.datasets import boston_housing #从Keras中导入mnist数据集
#读入训练集和测试集
(X_train, y_train), (X_test, y_test) = boston_housing.load_data()
print ("数据集张量形状:", X_train.shape) #shape方法显示张量的形状
print ("第一个数据样本:\n", X_train[0]) #注意Python的索引是从0开始的
print ("第101到200个数据样本:\n", X_train[100:199]) #注意Python的索引是从0开始的
print ("第一个数据样本的标签:", y_train[0])