项目01:牛刀小试—深度学习与计算机视觉入门基础

1. 创建环境和安装依赖

在前面的部分已经描述如何安装Anaconda和Jupyter notebook。Anaconda作为一个Python的发行版,其中包含了大量的科学包和自带环境管理的工具Conda,本书推荐使用Conda和Pip这两种方式去构建项目。

1.1 创建虚拟环境

Conda是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。

现在创建本书的第一个项目所需要用到的环境,我们将该环境命名为dlwork,采用Python3.6版本,打开终端,在命令行中输入 "conda create -n 环境名 python=版本号" 命令创建环境。(创建环境的步骤在本书安装和配置Anaconda章节中有详细的说明)

    (base) jingyudeMacBook-Pro:~ jingyuyan$ conda create -n keras python=3.6

创建环境完毕后,需要激活已创建的环境,使用“conda activate + 环境名”的方式激活:

    (base) jingyudeMacBook-Pro:~ jingyuyan$ conda activate dlwork

或者使用”source activate + 环境名“的方式进行激活:

    (base) jingyudeMacBook-Pro:~ jingyuyan$ source activate dlwork

1.2 安装依赖

在新的环境下安装jupyter notebook,这边推荐使用“conda install jupyter”命令进行安装:

    conda install jupyter

jupyter notebook安装完毕后,我们继续安装TensorFlow、Keras、OpenCV等一些环境依赖。

需要安装的依赖命令如下:

TensorFlow作为keras的backend,鉴于基础教程,本环境所使用的版本为CPU版本,后面的章节会讲述如何安装和配置GPU环境训练的安装,值得注意的是CPU版本下,使用conda安装的TensorFlow从1.9.0版本开始采用MKL-DNN,速度是与pip形式安装的TensorFlow相比和高达8倍,所以本书推荐使用conda install命令安装TensorFlow:

    conda install tensorflow

Keras可以作为TensorFlow的顶层Api接口简化了很多复杂算法的实现难度,可以使用更简洁的代码实现神经网络的搭建和训练,安装代码如下:

    conda install keras

OpenCV作为一款跨平台计算机视觉库,它在图像处理方面具有非常强大的功能,值得注意的是,新版的OpenCV4.x的版本与3.x的版本具有较大的差异,本书采用OpenCV3.4.20的版本:

    pip install opencv-python==3.4.5.20

Pandas是基于NumPy 的一种工具,纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具,安装方法如下:

    conda install pandas

安装完所有需要的依赖后可以使用“conda list”查看当前所安装的依赖情况

    conda list

2. 构建项目

在指定的磁盘路径创建存放当前项目的目录,linux或macos可使用mkdir命令创建文件夹目录,Windows直接使用图形化界面右键新建文件夹即可,例如我们的存放项目的目录名为project01:

   (dlwork) jingyudeMacBook-Pro:~ jingyuyan$ mkdir project01

创建成功后,在dlwork环境下,进入到project01目录下,打开jupyter notebook:

    cd project01

    jupyter notebook

新建一个新的ipynb文件,并且进入到文件中

3.数据操作——Numpy

Numpy是一个用python实现的科学计算,包括强大的N维数组对象Array、比较成熟的(广播)函数库、用于整合C/C++和Fortran代码的工具包、实用的线性代数、傅里叶变换和随机数生成函数。

本书仅简单的解释numpy的基本数据类型的使用操作与本书所需要使用到功能,具体更加详细的使用方法可以参考numpy官网中的教程:https://www.numpy.org.cn/

NumPy的主要对象是同种元素的多维数组。这是一个所有的元素都是一种类型、通过一个正整数元组索引的元素表格(通常是元素是数字)。在NumPy中维度(dimensions)叫做轴(axes),轴的个数叫做秩(rank)。

例如,在3D空间一个点的坐标[1, 2, 3]是一个秩为1的数组,因为它只有一个轴。那个轴长度为3.又例如,在以下例子中,数组的秩为2(它有两个维度).第一个维度长度为2,第二个维度长度为3.

3.1 多维数组的创建

NumPy的数组类被称作ndarray。通常被称作数组。注意numpy.array和标准Python库类array.array并不相同,后者只处理一维数组和提供少量功能。更多重要ndarray对象属性有:

  • ndarray.ndim

数组轴的个数,在python的世界中,轴的个数被称作秩

  • ndarray.shape

数组的维度。这是一个指示数组在每个维度上大小的整数元组。例如一个n排m列的矩阵,它的shape属性将是(2,3),这个元组的长度显然是秩,即维度或者ndim属性

  • ndarray.size

数组元素的总个数,等于shape属性中元组元素的乘积。

  • ndarray.dtype

一个用来描述数组中元素类型的对象,可以通过创造或指定dtype使用标准Python类型。另外NumPy提供它自己的数据类型。

  • ndarray.itemsize

数组中每个元素的字节大小。例如,一个元素类型为float64的数组itemsiz属性值为8(=64/8),又如,一个元素类型为complex32的数组item属性为4(=32/8).

  • ndarray.data

包含实际数组元素的缓冲区,通常我们不需要使用这个属性,因为我们总是通过索引来使用数组中的元素。

# 导入numpy包
import numpy as np

创建一个行向量

x = np.arange(9)
x
array([0, 1, 2, 3, 4, 5, 6, 7, 8])

可以看到返回了一个多维数组包含从0开始的9个连续的数。可以使用shape查看多维数组的形状。

x.shape
(9,)

也可以通过size属性查看多维数组的元素个数

x.size
9

下面通过使用reshape把上面定义的行向量x形状改成(3,3),也就是一个3行3列的方形矩阵X。矩阵内的元素保持向量中的元素不变。

X = x.reshape((3, 3))
X
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

可以发现,这时候可以发现向量已经发生了变化,转变成一个矩阵。上面的x.reshape((3,3))可以写成x.reshape((-1,3))。因为x的元素个数是已知的,-1是根据能够通过元素个数和其他维度大小推断出来。

下面,我们创建一个矩阵,包含各个元素为0, 形状为(2,4,5)的张量。之前创建的向量和矩阵,实际上都是特殊的张量表达形式。

np.zeros((4, 5))
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])

同理,我们可以创建各个元素为1的张量

np.ones((4, 5))
array([[1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.],
       [1., 1., 1., 1., 1.]])

当然,也可以创建随机元素的张量

np.random.rand(4, 5)
array([[0.83723537, 0.16645513, 0.3920299 , 0.15969092, 0.77422813],
       [0.84615548, 0.24549784, 0.02062126, 0.63891162, 0.50217548],
       [0.63705748, 0.79238493, 0.66458034, 0.23791342, 0.07209047],
       [0.40152764, 0.52363615, 0.18834408, 0.53714454, 0.53512496]])

利用Python的列表(list)转换成numpy的多维数组(ndarray)。

l = [[1,2,3],[4,5,6],[7,8,9]]
L = np.array(l)
L
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

3.2 多维数组的基本运算和操作方法

numpy支持大量运算符计算,首先定义好所需要用到的数组X和Y

X = np.arange(0, 20).reshape(4, 5)
X
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])
# arrange的第三个参数2表示间隔
Y =  np.arange(1, 40, 2).reshape(4, 5)
Y
array([[ 1,  3,  5,  7,  9],
       [11, 13, 15, 17, 19],
       [21, 23, 25, 27, 29],
       [31, 33, 35, 37, 39]])

做多维数组元素的加法运算

X + Y
array([[ 1,  4,  7, 10, 13],
       [16, 19, 22, 25, 28],
       [31, 34, 37, 40, 43],
       [46, 49, 52, 55, 58]])

做多数数组元素的乘法运算

X * Y
array([[  0,   3,  10,  21,  36],
       [ 55,  78, 105, 136, 171],
       [210, 253, 300, 351, 406],
       [465, 528, 595, 666, 741]])

做多数数组元素的除法运算

X / Y
array([[0.        , 0.33333333, 0.4       , 0.42857143, 0.44444444],
       [0.45454545, 0.46153846, 0.46666667, 0.47058824, 0.47368421],
       [0.47619048, 0.47826087, 0.48      , 0.48148148, 0.48275862],
       [0.48387097, 0.48484848, 0.48571429, 0.48648649, 0.48717949]])

可以看到除法运算结果会转换成浮点型

获得矩阵的转置

X.T
array([[ 0,  5, 10, 15],
       [ 1,  6, 11, 16],
       [ 2,  7, 12, 17],
       [ 3,  8, 13, 18],
       [ 4,  9, 14, 19]])

除了基本的元素计算以外,我们可以利用dot函数做矩阵或向量的乘法。

# 将X矩阵与Y的转置矩阵做乘法处理
X.dot(Y.T)
array([[  70,  170,  270,  370],
       [ 195,  545,  895, 1245],
       [ 320,  920, 1520, 2120],
       [ 445, 1295, 2145, 2995]])

同理,我们也可以通过连接函数(concatenate)来拼接两个矩阵。axis表示拼接矩阵的轴,如下是axis=0和axis=1所拼接的效果。注意,所需要拼接的轴不同的矩阵无法完成拼接。

np.concatenate([X, Y], axis=0)
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 1,  3,  5,  7,  9],
       [11, 13, 15, 17, 19],
       [21, 23, 25, 27, 29],
       [31, 33, 35, 37, 39]])
np.concatenate([X, Y], axis=1)
array([[ 0,  1,  2,  3,  4,  1,  3,  5,  7,  9],
       [ 5,  6,  7,  8,  9, 11, 13, 15, 17, 19],
       [10, 11, 12, 13, 14, 21, 23, 25, 27, 29],
       [15, 16, 17, 18, 19, 31, 33, 35, 37, 39]])

numpy也存在行拼接(column_stack)与列拼接(row_stack)的方法。

np.column_stack([X,Y])
array([[ 0,  1,  2,  3,  4,  1,  3,  5,  7,  9],
       [ 5,  6,  7,  8,  9, 11, 13, 15, 17, 19],
       [10, 11, 12, 13, 14, 21, 23, 25, 27, 29],
       [15, 16, 17, 18, 19, 31, 33, 35, 37, 39]])
np.row_stack([X,Y])
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [ 1,  3,  5,  7,  9],
       [11, 13, 15, 17, 19],
       [21, 23, 25, 27, 29],
       [31, 33, 35, 37, 39]])

3.3 多维数组索引

在Numpy中,多维数组的索引(index)代表了元素的位置。操作方式类似Python的索引,例如我们截取一个列表a中,从1到3的元素,方法为:a[1:3]。

下面我们定义一个多维数组尝试使用索引截取数据。

# 定义一个形状为(5,5)的0以此数到25的顺序元素的矩阵
X = np.arange(0, 25).reshape(5, 5)
X
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19],
       [20, 21, 22, 23, 24]])

我们截取该矩阵从1到4行的数据所以所形成如下结果

X[1:4]
array([[ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

若截取该矩阵从1到4行的数据的同时再截取1到4列的数据,则如下结果

X[1:4, 1:4]
array([[ 6,  7,  8],
       [11, 12, 13],
       [16, 17, 18]])

我们也可以访问单个元素,并进行赋值的操作

X[3, 4] = 100
X
array([[  0,   1,   2,   3,   4],
       [  5,   6,   7,   8,   9],
       [ 10,  11,  12,  13,  14],
       [ 15,  16,  17,  18, 100],
       [ 20,  21,  22,  23,  24]])

也可以截取部分元素,进行范围性的赋值操作

X[0:2, :] = 100
X
array([[100, 100, 100, 100, 100],
       [100, 100, 100, 100, 100],
       [ 10,  11,  12,  13,  14],
       [ 15,  16,  17,  18, 100],
       [ 20,  21,  22,  23,  24]])

numpy的基础部分便讲到这里,更多详情请到官网查阅官方文档

4. 线性回归

在讨论线性回归之前,有必要了解“回归”这个词在机器学习当中的含义。回归(regression)与我们平日里所说的"香港回归"中的回归(return)是完全不同的两个含义。它具有"倒推"的含义在其中。回归算法是相对于分类算法而言,与我们想要预测的目标变量y的类型有关。回归问题在生活中非常常见,例如预测房屋价格、气温、销售等连续值的问题。与回归问题不同的是分类(classification),分类问题所输出的结果是一个离散值,通常用于我们常见的图像分类、垃圾邮件识别,疾病检测等等输出为离散的问题都属于分类问题的范畴。

4.1 线性回归基本问题

我们以一个简单房屋价格预测作为例子来解释线性回归的基本问题。生活中的房价由多个问题,如房屋占地面积、地段、市场行情和开发商品牌效应等等因素所影响。为了简单的了解线性回归,我们仅以房屋占地面积作为影响房价的因数来探索房屋占地面积(平米)与房屋价格(万元)直接的具体关系。

以下是y的预测值的公式,w代表权重(weight), b表示偏差(bias)。y带个小帽子是表示对真实值y的值的预测,之所以为预测,说明它和真实值存在一定的误差。例如我们估算一个面积为60平米的房屋售价为81万或78万,而实际价格为80万,存在着一定的误差。

$$ \hat{y} = wx+b $$

4.2 线性回归从零开始实现

本小节将描述如何利用keras实现简单的一元线性回归。

本小节所需要使用到一些Keras的函数,作为牛刀小试的环节,带读者体会从零手动实现线性回归,下面的篇章将会详细介绍一些keras的函数如何使用。

4.2.1 导包

import keras
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense

4.2.2 构建数据集函数

这里我们构造一个简单人工数据集,能够使我们直观的比较学到参数和真实模型参数的区别。x表示随机生成的数据集,noise表示一个正态分布标准差为0.01的噪声,表示毫无意义的干扰。

def create_dataset(num = 100):
    x = np.random.rand(num)
    noise = np.random.normal(0, 0.01, x.shape)
    y = - 0.5 * x *  + 0.2 + noise
    return x, y

4.2.3 构建模型

构建一个keras的顺序模型,其中Dense表示全连接层,sgd表示随机梯度下降,误差采用mse(均方误差)。为什么使用mse作为损失函数,下一节会详细描述。

def build_model():
    model = Sequential()
    model.add(Dense(units=1,input_dim=1))
    model.compile(optimizer='sgd',loss='mse')
    return model

4.2.4 构建模型

构建一个训练函数,传入参数分别为模型、数据集x和真实结果集y作为参数。训练分为3000个迭代,每间隔500次输出一次当前训练结果。

def train(model, x, y):
    for step in range(3000):
        cost = model.train_on_batch(x, y)
        if step%500 == 0:
            print('cost:',cost)

4.2.5 构建可视化函数

利用matplotlib定义绘制可视化的视图函数, 参数x、y表示需要绘制的二维图像坐标,origin相似,但为绘制红点,通常表示绘制预测结果的点。

def show(x, y, origin=None):
    if origin is not None:
        plt.scatter(origin[0], origin[1])
        plt.plot(x,y,'r-',lw=2)
    else:
        plt.scatter(x, y)
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title('random')
    plt.show()

4.2.6 生成训练接

生成训练集,训练集中元素个数默认为100个元素,并且绘制出元素。

train_x, train_y = create_dataset()
show(train_x, train_y)

4.2.7 构建模型并训练

传递训练集中的数据,并进行训练,得出的模型作为结果保存着权重。可以见到每一次输出的训练结果误差都在下降

model = build_model()
train(model, train_x, train_y)
cost: 0.4223894
cost: 0.021369508
cost: 0.005917624
cost: 0.0016924017
cost: 0.00053704437
cost: 0.00022111919

4.2.8 预测训练结果

利用evaluate评估训练的结果,结果为误差值。

model.evaluate(train_x, train_y)
100/100 [==============================] - 0s 1ms/step





0.00013473228471411858

生成200个测试集数据,对已经训练好的模型进行测试集预测。并且绘制出结果。

test_x, test_y = create_dataset(200)
y_pred = model.predict(test_x)
show(test_x, y_pred, origin=(test_x, test_y))

可以看到图中所预测的结果中,中间一条红线是由预测的结果的坐标所构建出来的一条近似于直线的值,而围绕在周围的蓝点是通过附带偏差生成出来的测试集的结果。这样我们就通过Keras和numpy搭建了一个简单的线性回归模型,并进行了数据集测试。

4.3 损失函数

从上节内容中可发现,其实线性回归通俗上来描述,就是在现有一些数据中,尽可能的拟合出线性点。说白了,就是从一堆数据中找出一条直线出来。这条直线有非常多种的画法,例如我们给定几个点,并要求从中画出一条与各个点相关的直线:

以下是几种尝试,可以发现,三种方式画出的直线都是不一样的。

所以,既然是需要找到一条直线,那肯定需要有一个标准的评判方式,来评判哪条直线才是最好的。那么需要如何评判?回到刚刚房屋价格和房屋面积大小的问题当中,我们把所有预测房价与实际房价的价格的差距算出来后做个加和,就能量化出预测房价与实际房价直接存在的误差,例如下图中,每个小蓝点与直线的距离之间的一条小线就是实际结果和预测结果直接的差距。

这样的距离该如何计算?其实主要便是使用最常使用的欧几里得度量,也是叫欧氏距离:

结论

本篇主要简单的描述如何快速入门使用numpy创建多维数组和使用keras快速搭建神经网络并进行模型训练。下面的章节中会详细阐述如何更加深入的学习更多和神经网络相关的知识和实践。


版权声明:如无特殊说明,文章均为本站原创,转载请注明出处

本文链接:http://tunm.top/article/DeepLearning/