目录
前言
1机器学习概述
1.1机器学习简介
1.1.1机器学习背景
1.1.2机器学习简介
1.1.3机器学习简史
1.1.4机器学习主要流派
1.2机器学习、人工智能和数据挖掘
1.3典型机器学习应用领域
1.4机器学习算法分类
1.5机器学习的一般流程
1.5.1定义分析目标
1.5.2收集数据
1.5.3整理预处理
1.5.4数据建模
1.5.5模型训练
1.5.6模型评估
1.5.7模型应用
2机器学习的Python常用库
2.1Numpy简介及基本使用
2.1.1统计学
2.1.2大数据与统计学
统计学在机器学习中的应用
Numpy
Numpy安装
Numpy介绍
Numpy基本使用
2.2Pandas简介及基本使用
2.3Matplotlib简介及基本使用
2.4Scikit-Learn简介及基本使用
2.5波士顿房价预测实战
3回归算法与应用
4特征工程、降维与超参数调优
4.1特征工程
4.2降维与超参数调优
4.2.1数据降维
4.2.2超参数调优
5分类算法与应用
5.1分类问题简介
5.2k近邻算法
5.3概率模型和贝叶斯算法
5.4向量空间模型和支持向量机
5.5集成学习
6关联规则
7聚类算法与应用
7.1聚类算法概述与聚类中的数据机构
7.2划分聚类
7.3层次聚类
7.4密度聚类
8神经网络
8.1神经网络介绍及神经网络相关概念
8.2神经网络识别MINIS手写数据集
9文本分析
10图像数据分析
10.1图像数据概论
10.2图像识别案例-人脸识别
11深度学习入门
11.1深度学习概述与卷积神经网络
11.2循环神经网络
11.3深度学习流行框架
11.4基于卷积神经网络识别手写数字实战
机器学习是人工智能的重要技术基础,涉及的内容十分广泛。本文章涵盖了机器学习的基础知识,主要包括机器学习的概述、 回归、分类、聚类、神经网络、文本分析、图像分析、深度学习等经典的机器学习基础知识,还包括深度学习入门等拔高内容。
介绍机器学习的基础概念和知识,包括机器学习简史、主要流派、与人工智能、数据挖掘的关系、应用领域、算法、一般流程等。
伴随着计算机计算能力的不断提升以及大数据时代的迅发展人工智能也取得了前所未有的进步。
很多企业均开始使用机器学习的相关技术于大部分行业中,以此获得更为强大的洞察力,也为企业的日常生活和企业运营带来了很大的帮助,从而提高了整个产品的服务质量。
机器学习的典型应用领域有:搜索引擎、自动驾驶、量化投资、计算机视觉、信用卡欺诈检测、游戏、数据挖掘、电子商务、图像识别、自然语言处理、医学诊断、证券金融市场分析以及机器人等相关领域,故在一定程度上,机器学习相关技术的进步也提升了人工智能领域发展的速度。
机器学习(MachineLearning),作为计算机科学的子领域,是人工智能领域的重要分支和实现方式。
机器学习的思想:计算机程序随着经验的积累,能够实现性能的提高。对于某一类任务T及其性能度量P,若一个计算机程序在T上以P衡量的性能随着经验E而自我完善,那么就称这个计算机程序在从经验E学习。
主要的基础理论:数理统计,数学分析,概率论,线性代数,优化理论,数值逼近、计算复杂性理论。
机器学习的核心元素:算法、数据以及模型。
作为一门不断发展的学科,机器学习尽管在最近几年才发展成为一门独立的学科。
起源于20世纪50年代以来人工智能的逻辑推理、启发式搜索、专家系统、符号演算、自动机模型、模糊数学以及神经网络的反向传播BP算法等。如今作为机器学习重要的基础理论。
在1950年代,已经有了机器学习的相关研究。代表工作主要是F.Rosenblatt基于神经感觉科学提出的计算机神经网络,即感知器。随后十年,用于浅层学习的神经网络风靡一时,尤其是MarvinMinsky提出了著名的XOR问题和感知器线性度不可分割的问题。
局限:由于计算机的计算能力有限,因此很难训练多层网络。通常使用仅具有一个隐藏层的浅层模型。尽管已经陆续提出了各种浅层机器学习模型,但理论分析和应用方面都已产生。但是,理论分析和训练方法的难度要求大量的经验和技能。而随着最近邻算法和其他算法的相继提出,在模型理解,准确性和模型训练方面已经超越了浅层模型。机器学习的发展几乎停滞不前。
在2006年,希尔顿(Hinton)发表了一篇关于深度信念网络的论文,Bengio等人发表了关于“深度网络的贪婪分层明智训练”的论文,而LeCun团队发表了基于能量模型的“稀疏表示的有效学习”。
这些事件标志着人工智能正式进入深度网络的实践阶段。同时,云计算和GPU并行计算为深度学习的发展提供了基本保证,尤其是近年来,机器学习它在各个领域都实现了快速发展。新的机器学习算法面临的主要问题更加复杂。机器学习的应用领域已从广度发展到深度,这对模型的训练和应用提出了更高的要求。
随着人工智能的发展,冯·诺依曼有限状态机的理论基础变得越来越难以满足当前神经网络中层数的要求。这些都给机器学习带来了挑战。
在人工智能的发展中,随着人们对智能的理解和对实际问题的解决方案的发展,机器学习大致出现了符号主义、贝叶斯、联结主义、进化主义、行为类推主义五大流派。
符号主义起源于逻辑和哲学,其实现方法是利用符号来表达知识并使用规则进行逻辑推理。专家系统和知识工程是该理论的代表。符号主义学派认为,知识是信息符号的表示,是人工智能的基础。这些符号被输入到计算机中进行仿真和推理,以实现人工智能。
贝叶斯定理是概率论中的一个定理,其中P(A|B)是事件B发生时事件A发生的概率(条件概率)。贝叶斯学习已被应用于许多领域。例如,自然语言中的情感分类,自动驾驶和垃圾邮件过滤。
联结主义起源于神经科学,主要算法是神经网络,它由一定结构中的大量神经元组成。神经元是一种看起来像树的细胞,它由细胞主体和细胞突起组成,长轴突被鞘覆盖以形成神经纤维,在其末端的小分支称为神经末梢。每个神经元可以具有一个或多个树突,这些树突可以接受刺激并将兴奋转移到细胞体内。每个神经元只有一个轴突,它可以将兴奋从细胞体传递到另一个神经元或其他组织,神经元相互连接,从而形成一个大型的神经网络,人类所学到的几乎所有知识都存在其中,如下图所示:
在神经网络中,将n个连接的神经元的输出用作当前神经元的输入,进行加权计算,并添加一个偏置值(Bias)以通过激活函数实现变换,激活功能的功能是在一定范围内输出控制。以Sigmoid函数为例,输入是从负无穷大到正无穷大,并在激活后映射到(0,1)间隔。
人工神经网络是分层(Layer)组织的,每层包含多个神经元,这些层通过某种结构连接,神经网络训练的目的是找到网络中的每个突触连接的权重和偏差值。作为一种监督学习算法,神经网络的训练过程是通过不断反馈当前网络计算结果与训练数据之间的误差来校正网络权重,使得误差足够小,这就是反向传播算法。
1850年,达尔文提出进化论。进化过程是适者生存的过程,个体生物在其中适应环境。智能需要适应不断变化的环境,并通过对进化过程进行建模来生成智能行为。
进化算法(EA)是基于“自然选择,适者生存”和迭代优化的原理,在计算机上模拟进化过程,直到找到最佳结果。进化算法包括基本操作,例如基因编码,群体初始化和交叉变异算子。它是一种相对成熟的全局优化方法,具有广泛的适用性。它具有自组织,自适应和自学习的特征,可以有效地处理传统优化。用算法难以解决的复杂问题(例如NP硬优化问题)。
遗传算法的优化应根据具体情况选择算法,也可以与其他算法结合进行补充。对于动态数据,可能难以使用遗传算法来找到最佳解,并且种群可能会过早收敛。
根据约束条件优化功能,行为类比主义者倾向于通过类比推理获得知识和理论,并在未知情况和已知情况之间建立相应的关系。在实际应用中,是计算它们之间的相似度,然后定义关联关系。
明确目标任务是第一个需求,也是选择合适的机器学习算法的关键所在。通过阐明业务需求以及要解决的实际问题,才能根据现有的数据进行模型的设计以及算法的选择。
在监督学习中,分类算法用于定性问题,而回归方法用于定量分析。
在无监督学习中,如果存在样本分割,则可以应用聚类算法。如果需要找出各种数据项之间的内部联系,则可以应用关联分析。
1.数据应具有代表性,并尽可能地覆盖区域,不然的话,可能出现过拟合和欠拟合的情况。
2.样本数据应平衡。在分类问题的范畴中,如果存在不同类别之间的样本比例较大的情况或者样本数据不平衡的现象,均会影响最终模型的性能。
3.评估数据的量级,包括特征的数量以及样本的数量。根据这些指标估计数据和分析对内存的消耗,并判断在训练过程中内存是否过大,如果内存过大则需要对算法进行优化、改进,或者通过对某些降维技术的使用实现内存消耗合理化,必要的话甚至还会采用一些分布式机器学习的技术。
1.数据探索
首先通过对数据进行一定的探索,了解数据的基本结构,数据的统计信息,数据噪声和数据分布等相关信息。
为了更好地对数据的状况进行查看以及数据模式的获取,可以采用数据质量评估以及数据可视化等相关方法来评估数据的质量。
2.数据处理
经过步骤1,可能会发现很多数据质量的问题,例如缺失值,不规则的数据,数据的分布不平衡,数据异常和数据冗余等问题。这些问题的存在将严重降低数据的质量。
数据预处理的操作也是非常重要,在生产环境中的机器学习中,数据通常是原始的,未经过加工以及处理的,而数据预处理的工作通常占据着整个机器学习过程中的绝大部分时间。
常见的数据预处理的方法:缺失值处理,离散化,归一化,去除共线性等方法是机器学习算法。整理预处理
采用特征选择的方法,可以实现从大量的数据中提取适当的特征,并将选择好的特征应用于模型的训练中,以获得更高精度的模型。
筛选出显著特征需要对业务有非常充分的了解并分析数据。特征选择是否合适通常会对模型的精度有非常直接的影响。选择好的特征,即使采用较为简单的算法,也可以获得较为稳定且良好的模型。
特征有效性分析的技术:相关系数、平均互信息、后验概率、卡方检验、条件熵、逻辑回归权重等方法。
在训练模型之前,通常将数据集分为训练集与测试集,有的时候,会将训练集继续细分为训练集和验证集,以评估模型的泛化能力。
模型本身不存在好坏之分。在进行模型的选择时,通常,没有哪一种算法在任何情况下都能够表现良好,在实际进行算法的选择时,通常,采用几种不同的算法同时进行模型的训练,之后再比较它们之间的性能,并选择其中表现最佳的算法。
不同的模型采用不同的性能指标。
在模型训练的过程中,需要调整模型的超参数。
在训练的过程中,对机器学习算法的原理以及其推导的过程的要求越高,对机器学习算法的了解越深,就越容易找到问题出现的原因,从而进行合理的模型调整。模型训练
利用测试集数据对模型的精度进行评估与测验,以便评估训练模型对新数据的泛化能力。
假如评估的效果不是很理想,那么就需要分析模型效果不理想的原因并对训练模型进行一定的优化与改进,例如手动调整参数等改进方法。
评估不理想,需要首先诊断模型以确定模型调整的正确思路与方向。过度拟合和欠拟合问题的判断是模型诊断中的重要步骤。
典型方法:绘制学习曲线和交叉验证。
如何解决:
出现过度拟合问题时,其模型的基本调整策略是在增加数据量的同时能够降低模型的复杂度,也可以采用正则化的方法来提高训练模型的泛化能力。
对于模型欠拟合的问题,其模型的基本调整策略是在增加特征数量和质量的同时也增加模型的复杂度。
误差分析是通过对产生误差的样本进行观察并且分析误差的原因。
误差分析的过程:由数据质量的验证,算法选择的验证,特征选择的验证,参数设置的验证等几部分。对数据质量的验证非常重要,通常对参数进行反复地调整,在调整了很长时间之后,才发现数据预处理效果不佳,数据的质量存在一定的问题。
调整模型后,需要对其进行重新训练以及模型评估。
建立机器学习模型的过程也是不断尝试的过程,直至最后模型达到最佳且最稳定的状态。
在工程实施方面,主要通过预处理、特征清理以及模型集成等方式来提高算法的精确度以及泛化能力。
通常,直接对参数进行调整的工作不是太多。因为当数据的量级达到一定的程度时,其训练的速度非常地缓慢,并且不能保证效果。
模型的应用主要和工程的实施有很大的关系。
工程以结果为导向的,模型在线执行的效果与模型的质量有着非常直接的关系,不仅简单地包括其准确性,误差等方面的信息,还包括其资源消耗的程度(空间复杂度)、运行速度(时间复杂度)以及稳定性是否可以接受等方面的问题。
在机器学习和人工智能领域,Python是最受欢迎的编程语言之一。Python的设计哲学是‘优雅’、‘明确’、’简单‘,属于通用型的编程语言。本章介绍机器学习常用的几个python库及其基础使用。
统计学是关于认识客观现象总体数量特征和数量关系的科学
统计学的一个案例:子弹中的统计学
回想刚才例子,统计需基于大量的数据进行验证
同样的,数据也可以基于统计进行分析
文本统计与文学作品鉴真
文本统计设计到的指标词频率、平均句长、平均词长
统计学常用指标
平均数
绝对数与相对数
百分比
频率
同比与环比
Numpy的全称是NumericalPython,作 为 高 性 能 的 数 据 分 析 以 及 科 学 计 算 的 基 础 包,Numpy提供了矩阵科学计算的相关功能。Numpy提供的功能主要分为以下几个:
1.提供了数组数据快速进行标准科学计算的相关功能。
2.提供了有用的线性代数,傅里叶变换和随机数的相关功能。
3.ndarray—一个具有向量算术运算和复杂广播能力的多维数组对象。
4.用于读写磁盘数据的工具以及用于操作内存映射文件的工具。
5.提供了集成Fortran以及C/C++代码的工具。
【注】上述所提及“广播”的意思可以理解为:当存在两个不同维度数组(array)进行科学运算时,由于Numpy运算时需要相同的结构,可以用低维的数组复制成高维数组参与运算。
Python官网上的发行版是不包含NumPy模块的,即如果使用Numpy需要自行安装,安装的方式有以下几种:
(1)使用pip安装
使用pip工具进行NumPy的安装是最简单且快速的方法,使用如下命令即可完成安装:
pipinstall--usernumpy
--user选项的功能是可以设置numpy只安装在当前用户下,而不是写入到系统目录中。该命令在默认情况下使用的是国外线路,速度很慢,故推荐使用清华镜像进行下载并安装:
pipinstallnumpy-ihttps://pypi.tuna.tsinghua.edu.cn/simple
(2)使用已有的发行版本
对于大多数用户,尤其是在Windows操作系统上,其实最简单的方法是下载Anaconda Python发行版,因为anaconda集成了许多数据科学计算的关键包(包括NumPy,SciPy,matplotlib,IPython,SymPy以及Python核心自带的其它包)。
Anaconda:是开源且免费Python发行版,适用于大规模数据处理、预测分析,和科学计算,实现包的简化管理和部署,并且支持Linux,Windows和Mac等系统。
通过该实验的实践,要求大家可以掌握不同维度数组的表示形式,熟悉ndarray的属性和基本操作,能够使用Numpy进行数组的运算、统计和数据存取等操作。
ndarray的属性和基本操作
创建一个numpy.ndarray对象:
>>>import numpyas np
>>>a= np.array([[1,2,3],[4,5,6]])
>>>a
运行结果如下:
array([[1, 2, 3],[4, 5, 6]])
ndarray对象的别名是array:
>>>type(a)
运行结果如下:
numpy.ndarray
确定各个维度的元素个数:
>>>a.shape
运行结果如下:
(2, 3)
元素个数:
>>>a.size
运行结果如下:
6
数据的维度:
>>>a.ndim
运行结果如下:
2
数据类型:
>>>a.dtype
运行结果如下:
dtype('int32')
每个元素的大小,以字节为单位:
>>>a.itemsize
运行结果如下:
2
访问数组的元素:
>>>a[0][0]
运行结果如下:
1
从列表创建:
>>>import numpyas np
>>>np.array([[1,2,3],[4,5,6]],dtype=np.float32)
运行结果如下:
array([[1., 2., 3.],[4., 5., 6.]], dtype=float32)
从元组创建
>>>np.array([(1,2),(2,3)])
运行结果如下:
array([[1, 2],[2, 3]])
从列表和元组创建
>>>np.array([[1,2,3,4],(4,5,6,7)])
运行结果如下:
array([[1, 2, 3, 4],[4, 5, 6, 7]])
类似range()函数,返回ndarray类型,元素从0到n‐1
>>>np.arange(5)
运行结果如下:
array([0, 1, 2, 3, 4])
(1)切片
Numpy支持切片操作,以下为相关例子说明:
import numpyas np
matrix=np.array([[10,20,30],[40,50,60],[70,80,90]])
print(matrix[:,1])print(matrix[:,0:2])
print(matrix[1:3,:])
print(matrix[1:3,0:2])
运行结果:
[20 50 80][[10 20][40 50][70 80]][[40 50 60][70 80 90]][[40 50][70 80]]
(2)数组比较
Numpy也提供了较为强大的矩阵和数组比较功能,对于数据的比较,最终输出的结果为boolean值。为了方便理解,举以下例子来说明:
import numpyas np
matrix=np.array([[10,20,30],[40,50,60],[70,80,90]])
m=(matrix==50)
print(m)
运行结果:
[[False False False][False True False][False False False]]
我们再来看一个比较复杂的例子:
import numpyas np
matrix=np.array([[10,20,30],[40,50,60],[70,80,90]])
second_column_50=(matrix[:,1]==50)
print(second_column_50)
print(matrix[second_column_50,:])
运行结果:
[False True False][[40 50 60]]
(3)替代值
NumPy可以运用布尔值来替换值。
在数组中:
import numpy
vector =numpy.array([10,20,30,40])
equal_to_ten_or_five=(vector==20)|(vector==20)
vector[equal_to_ten_or_five]=200
print(vector)
运行结果:
[ 10 200 30 40]
在矩阵中:
import numpy
matrix=numpy.array([[10,20,30],[40,50,60],[70,80,90]])
second_column_50=matrix[:,1]==50
matrix[second_column_50,1]=20
print(matrix)
运行结果:
[[10 20 30][40 20 60][70 80 90]]
这里,我们演示把空值替换为“0”的操作。
import numpyas np
matrix=np.array([['10','20','30'],['40','50','60'],['70','80','']])
second_column_50=(matrix[:,2]=='')
matrix[second_column_50,2]='0'
print(matrix)
运行结果:
[['10' '20' '30']['40' '50' '60']['70' '80' '0']]
(4)数据类型转换
在Numpy当中,ndaray数组的数据类型可以使用dtype参 数 进 行 设 置,还可以通过astype方法进行数据类型的转换,该方法在进行文件的相关处理时很方便、实用,值得注意的是,使用astype()方法对数据类型进行转换时,其结果是一个新的数组,可以理解为对原始数据的一份复制,但不同的是数据的数据类型。
比如,把String转换成float。如下:
import numpy
vector=numpy.array(["22","33","44"])
vector=vector.astype(float)
print(vector)
运行结果:
[22. 33. 44.]
(5)Numpy的统计计算方法
Numpy基本使用除了以上介绍的相关功能,Numpy还内置了更多地科学计算的方法,尤其是最为重要的统计方法,如下:
1.max():用于统计计算出数组元素当中的最大值;对于矩阵计算结果为一个一维数组,需要指定行或者列。
2.mean():用于统计计算数组元素当中的平均值;对于矩阵计算结果为一个一维数组,需要指定行或者列。
3.sum():用于统计计算数组元素当中的和;对于矩阵计算结果为一个一维数组,需要指定行或者列。
值得注意的是,用于这些统计方法计算的数值类型必须是int或者float。
数组例子:
import numpy
vector=numpy.array([10,20,30,40])
print(vector.sum())
运行结果:
100
矩阵例子:
import numpyas np
matrix=np.array([[10,20,30],[40,50,60],[70,80,90]])
print(matrix.sum(axis=1))
print(np.array([5,10,20]))
print(matrix.sum(axis=0))
print(np.array([10,10,15]))
运行结果:
[ 60 150 240][ 5 10 20][120 150 180][10 10 15]
Pandas简介
官网链接:http://pandas.pydata.org/
简介:Pandas是python的一个数据分析包,最初由AQR Capital Management于2008年4月开发,并于2009年底开源出来,目前由专注于Python数据包开发的PyData开发team继续开发和维护,属于PyData项目的一部分。Pandas最初被作为金融数据分析工具而开发出来,因此,pandas为时间序列分析提供了很好的支持。
强大的Pandas
基本功能
开发pandas时提出的需求
具备按轴自动或显式数据对齐功能的数据结构
集成时间序列功能
既能处理时间序列数据也能处理非时间序列数据的数据结构
数学运算和约简(比如对某个轴求和)可以根据不同的元数据(轴编号)执行
灵活处理缺失数据
合并及其他出现在常见数据库(例如基于SQL的)中的关系型运算
数据结构
Series(一维)
DataFrame(二维)
Panel(三维)
数据结构Series
Series是一种类似于一维数组的对象,它由一组数据(各种NumPy数据类型)以及一组与之相关的数据标签(即索引)组成
Series的字符串表现形式为:索引在左边,值在右边
三方面来了解Series
创建
读写
运算
数据结构Series的创建
>>>ser1= Series(range(4))
>>>ser2 = Series(range(4),index = ["a","b","c","d"])
>>>sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
>>>ser3 = Series(sdata)
注:传递的data是一个dict字典类型对象,并且传递了index参数,那么对应的值将从字典中取出。否则,index的值将由字典对象里的key值进行构造
Series的index和values
>>>ser2.index
Index(['a', 'b', 'c', 'd'], dtype='object')
>>>ser2.values
array([0, 1, 2, 3])
>>>ser2[["a","c"]]
a 0
c 2
数据结构Series的读写
>>>a = pd.Series([11, 22, 33, 44, 55])
>>>a[1:3]
1 22
2 33
>>>a[1]=2
>>>a[1:3]
1 2
2 33
数据结构Series的运算
Series间的计算
>>>a = pd.Series([1, 2, 3, 4])
>>>b = pd.Series([1, 2, 1, 2])
>>>print(a + b)
>>>print(a *2)
>>>print(a >= 3)
>>>print(a[a >= 3])
Series函数的使用
>>>a = pd.Series([1, 2, 3, 4, 5])
平均值
>>>print(a.mean())
数据结构DataFrame
DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)
DataFrame既有行索引也有列索引,它可以被看做由Series组成的字典(共用同一个索引)
几方面来了解DataFrame
创建
索引
apply方法
算术运算
缺失值处理
数据结构DataFrame的创建
二维的ndarray
>>>import pandas as pd
>>>df1=pd.DataFrame(np.arange(9).reshape(3,3),columns=list('bcd'),index=['b','s','g'])
外部导入
>>>import pandas as pd
>>>df = pd.read_csv('./data/titanic.csv')
字典导入
>>>import pandas as pd
>>>data = {'country':['aaa','bbb','ccc'], 'population':[10,12,14]}
>>>df_data = pd.DataFrame(data)
使用Series导入
>>>d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), 'two': pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
>>>df = pd.DataFrame(d)
>>>pd.DataFrame(d, index=['d', 'b', 'a'])
数据结构DataFrame的查看
>>>df.head(6)
>>>df.info()
>>>df.index
>>>df.columns
>>>df.dtypes
>>>df.values
数据结构的索引对象
pandas的索引对象负责管理轴标签和其他元数据(比如轴名称等)。构建Series或DataFrame时,所用到的任何数组或其他序列的标签都可以被转换成一个Index。
指定索引
>>>df = df.set_index('Name')
pandas中主要的index对象
Index的方法和属性1
Index的方法和属性2
索引操作
loc用label来去定位,iloc用position来去定位
>>>df.iloc[0:5,1:3]
>>>df = df.set_index('Name')
>>>df.loc['Heikkinen, Miss. Laina']
>>>df.loc['Heikkinen, Miss. Laina':'Allen, Mr. William Henry']
重新索引
创建一个适应新索引的新对象,该Series的reindex将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值
reindex函数的参数
reindex操作
>>>s1 = pd.Series(['a','b','c','d','e'],index=[2,1,3,5,4])
>>>s2 = s1.reindex([1,2,3,4,5,6],fill_value=0)
>>>s3 = s2.reindex(range(10),method='bfill')
groupby操作
>>>df = pd.DataFrame({'key':['A','B','C','A','B','C','A','B','C'], 'data':[0,5,10,5,10,15,10,15,20]})
>>>df.groupby('key').sum()
>>>df = pd.read_csv('./data/titanic.csv')
>>>df.groupby('Sex')['Age'].mean()
函数应用和映射
DataFrame的apply方法(index)
对象的applymap方法(因为Series有一个应用于元素级的map方法)
DataFrame的apply方法
>>>def plus(df,n):
>>>df['c'] = (df['a']+df['b'])
>>>df['d'] = (df['a']+df['b']) * n
>>>return df
>>>list1 = [[1,3],[7,8],[4,5]]
>>>df1 = pd.DataFrame(list1,columns=['a','b'])
>>>df1 = df1.apply(plus,axis=1,args=(2,))
>>>print(df1)
DataFrame的applymap方法
对象的applymap方法(因为Series有一个应用于元素级的map方法)
apply对一个列进行整体运算
applymap对一个DataFrame中的每个元素进行转换
map对一个Series中的每个元素进行转换
排序和排名
对行或列索引进行排序
对于DataFrame,根据任意一个轴上的索引进行排序
可以指定升序降序
按值排序
对于DataFrame,可以指定按值排序的列
rank函数
>>>df = pd.DataFrame([1, 2, 3, 4, 5], index=[10, 52, 24, 158, 112], columns=['S'])
>>>df.sort_index(inplace=True)
对于DataFrame,可以指定按值排序的列
>>>frame = pd.DataFrame({'b':[4, 7, -3, 2], 'a':[0, 1, 0, 1]})
>>>frame.sort_values(by='b')
算术运算
对不同的索引对象进行算术运算
对齐操作会同时发生在行和列上,把2个对象相加会得到一个新的对象,其索引为原来2个对象的索引的并集:
>>>df1 = pd.DataFrame(np.arange(9).reshape(3,3),columns=list('bcd'),index=['b','s','g'])
>>>df2 = pd.DataFrame(np.arange(12).reshape(4,3),columns=list('cde'),index=['b','s','g','t'])
>>>df1+df2
>>>df3 = df1.add(df2,fill_value='0')
汇总和计算描述统计
常用描述和汇总统计函数
唯一值以及成员资格
缺失数据处理
NaN(Not a Number)表示浮点数和非浮点数组中的缺失数据
None也被当作NAN处理
处理缺失数据滤除缺失数据方法
dropna()
drop()
how参数控制行为,axis参数选择轴,thresh参数控制留下的数量
处理缺失数据滤除缺失数据
>>>ser = pd.Series([4.5,7.2,-5.3,3.6], index=['d','b','a','c'])
>>>ser.drop('c')
>>>from numpyimport NaN
>>>se1=pd.Series([4,NaN,8,NaN,5])
>>>se2 = se1.dropna()
>>>se2
>>>df = DataFrame(np.arange(9).reshape(3,3), index=['a','c','d'], columns=['oh','te','ca'])
>>>df.drop('a')
>>>df.drop(['oh','te'],axis=1)
>>>df1=pd.DataFrame([[1,2,3],[NaN,NaN,2],[NaN,NaN,NaN],[8,8,NaN]])
>>>df1.dropna()
>>>df1.dropna(how='all')
处理缺失数据填充缺失数据
fillna
inplace参数控制返回新对象还是就地修改
>>>from numpy import nan as NaN
>>>df1=pd.DataFrame([[1,2,3],[NaN,NaN,2],[NaN,NaN,NaN],[8,8,NaN]])
>>>df1.fillna(100)
>>>df1.fillna({0:10,1:20,2:30})
传入inplace=True直接修改原对象
>>>df1.fillna(0,inplace=True)
>>>df2=pd.DataFrame(np.random.randint(0,10,(5,5)))
>>>df2.iloc[1:4,3]=NaN
>>>df2.iloc[2:4,4]=NaN
>>>df2.fillna(method='ffill')#用前面的值来填充
>>>df2.fillna(method='bfill',limit=2) #传入limit=” “限制填充个数:
>>>df2.fillna(method="ffill",limit=1,axis=1) #传入axis=” “修改填充方向:
实验实现Pandas自行车数据分析
假设我们现在有些自行车行驶数据,这组数据记录的是蒙特利尔市内七条自行车道的自行车 骑 行 人 数,让 我 看 看 我 们 能 用Pandas分 析 出 一 些 什 么 吧,原 始 数 据 集bikes.csv可 以 在Pandas官方网址上进行下载。
(1)导入Pandas:
>>>import pandas as pd
(2)准备画图环境:
>>>import matplotlib.pyplotas plt
>>>pd.set_option('display.mpl_style', 'default')
>>>plt.rcParams['figure.figsize'] = (15, 5)
(3)使用read_csv函数读取csv文件,读取一组自行车骑行数据,得到一个DataFrame对象:
# 使用latin1编码读入,默认的utf-8编码不适合
>>>broken_df= pd.read_csv('bikes.csv', encoding='latin1')
# 查看表格的前三行
>>>broken_df[:3]
Date;Berri1;Brébeuf (donnéesnon disponibles);Côte-Sainte-Catherine;Maisonneuve1;Maisonneuve 2;du Parc;Pierre-Dupuy;Rachel1;St-Urbain (donnéesnon disponibles)
0 01/01/2012;35;;0;38;51;26;10;16;
1 02/01/2012;83;;1;68;153;53;6;43;
2 03/01/2012;135;;2;104;248;89;3;58;
(4)对比原始文件的前五行即图2-1和导入的DataFrame数据结构,我们发现读入的原始数据存在以下两个问题:即使用“;”作为分隔符(不符合函数默认的“,”作为分隔符)和首列的日期文本格式为xx/xx/xxxx(不符合Pandas的时间日期格式)。
head-n5bikes.csv
(5)修复读入问题:
1)使用“;”作为分隔符
2)解析Date列(首列)的日期文本
3)设置日期文本格式
4)使用日期列作为索引
>>>fixed_df=pd.read_csv('bikes.csv',encoding='latin1',sep=';',parse_dates=['Date'],dayfirst=True,index_col='Date')
>>>fixed_df[:3]
Berri 1 Brébeuf (données non disponibles) Côte-Sainte-Catherine \
Date
2012-01-01 35 NaN 0
2012-01-02 83 NaN 1
2012-01-03 135 NaN 2
Maisonneuve 1 Maisonneuve 2 du Parc Pierre-Dupuy Rachel1 \
Date
2012-01-01 38 51 26 10 16
2012-01-02 68 15 35 36 43
2012-01-03 104 248 89 3 58
St-Urbain(données non disponibles)
Date
2012-01-01 NaN
2012-01-02 NaN
2012-01-03 NaN
(6)读取csv文件所得结果是一个DataFrame对象,每列对应一条自行车道,每行对应一天的数据,我们从DataFrame中选择一列,使用类似于字典(dict)的语法访问选择其中的一列:
>>>fixed_df['Berri 1']
Date
2012-01-01 35
2012-01-02 83
2012-01-03 135
2012-01-04 144
2012-01-05 197
2012-01-06 146
2012-01-07 98
2012-01-08 95
2012-01-09 244
2012-01-10 397
2012-01-11 273
2012-01-12 157
2012-01-13 75
2012-01-14 32
2012-01-15 54
2012-01-16 168
2012-01-17 155
2012-01-18 139
2012-01-19 191
2012-01-20 161
2012-01-21 53
2012-01-22 71
2012-01-23 210
2012-01-24 299
2012-01-25 334
2012-01-26 306
2012-01-27 91
2012-01-28 80
2012-01-29 87
2012-01-30 219
......
2012-10-07 1580
2012-10-08 1854
2012-10-09 4787
2012-10-10 3115
2012-10-11 3746
2012-10-12 3169
2012-10-13 1783
2012-10-14 587
2012-10-15 3292
2012-10-16 3739
2012-10-17 4098
2012-10-18 4671
2012-10-19 1313
2012-10-20 2011
2012-10-21 1277
2012-10-22 3650
2012-10-23 4177
2012-10-24 3744
2012-10-25 3735
2012-10-26 4290
2012-10-27 1857
2012-10-28 1310
2012-10-29 2919
2012-10-30 2887
2012-10-31 2634
2012-11-01 2405
2012-11-02 1582
2012-11-03 844
2012-11-04 966
2012-11-05 2247Name: Berri 1, Length: 310, dtype: int64
(7)将所选择的列绘成图,可以直观地看出骑行人数的变化趋势:
(8)绘制所有的列(自行车道)如下图,可以看到,每条车道的变化趋势都是类似的:
(9)假设我们住在蒙特利尔,我很好奇人们是在周末还是在工作日骑自行车?在我们的数据结构dataframe中添加一个“工作日”列,下一步,我们查看贝里自行车路径数据。贝里是蒙特利尔的一条街道,有一条非常重要的自行车道。我会去图书馆的路上骑自行车,有时去旧蒙特利尔工作的时候也常常骑自行车。因此,我们将创建一个数据框,其中只有贝里(BiKePath):
>>>berri_bikes= fixed_df[['Berri 1']].copy()
>>>berri_bikes[:5]
Berri 1
Date
2012-01-01 35
2012-01-02 83
2012-01-03 135
2012-01-04 144
2012-01-05 197
(10)接下来,我们需要添加一个“工作日”列。首先,我们可以从索引中获得工作日。我们还没有谈到索引,但是索引是在上面的数据框的左边,在“日期”下。基本上是一年中的所有日子:>>>berri_bikes.index
DatetimeIndex(['2012-01-01','2012-01-02','2012-01-03','2012-01-04','2012-01-05','2012-01-06','2012-01-07','2012-01-08','2012-01-09','2012-01-10',...'2012-10-27','2012-10-28','2012-10-29','2012-10-30','2012-10-31','2012-11-01','2012-11-02','2012-11-03','2012-11-04','2012-11-05'],dtype='datetime64[ns]',name='Date',length=310,freq=None)
(11)我们可以看到实际上数据是不完整的,通过length数据可以发现,一年只有310天。为什么呢?Pandas时间序列功能非常的强大,所以如果我们想得到每一行的月份,我们可以输入以下语句:
>>>berri_bikes.index.day
Int64Index([1,2,3,4,5,6,7,8,9,10,...27,28,29,30,31,1,2,3,4,5],dtype='int64',name='Date',length=310)
(12)为普通日进行索引设置:
>>>berri_bikes.index.weekday
Int64Index([6,0,1,2,3,4,5,6,0,1,...5,6,0,1,2,3,4,5,6,0],dtype='int64',name='Date',length=310)
(13)通过上面的语句,这我们获得了一周中的单位日,我们通过与日历进行对比会发现数据中的0代表的是星期一。我们现在已经知道如何设置普通日进行索引了,我们接下来需要做的是把普通日的索引设置为dataframe中的一列:
>>>berri_bikes.loc[:,'weekday']=berri_bikes.index.weekday
>>>berri_bikes[:5]
Berri 1 weekday
Date
2012-01-01 35 6
2012-01-02 83 0
2012-01-03 135 1
2012-01-04 144 2
2012-01-05 197 3
(14)接下来我们就可以把普通日作为一个统计日进行骑行人数的统计了,而这在Pandas中实现也非常简单,Dataframes中 存 在 一 个groupby()方法,这 个 方 法 有 点 类 似 与SQL语 句 中 的groupby方法。而 实 现 的 语 句 是,weekday_counts=berri_bikes.groupby('weekday').aggregate(sum),语句的目的是把Berri车道数据按照相同普通日的标准进行分组并累加:
>>>weekday_counts= berri_bikes.groupby('weekday').aggregate(sum)
>>>weekday_counts
Berri 1
weekday
0 134298
1 135305
2 152972
3 160131
4 141771
5 101578
6 99310
(15)这时候我们会发现通过0,1,2,3,4,5,6这样的数字很难记住其相对应的日子,我们可以通过以下方法修改:
>>>weekday_counts.index=['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
>>>weekday_counts
Berri 1
Monday 134298
Tuesday 135305
Wednesday 152972
Thursday 160131
Friday 141771
Saturday 101578
Sunday 99310
(16)通过直方图2-7看看统计情况:通过结果我们发现Montrealers似乎是一个喜欢使用自行车作为通勤工具的城市即人们在工作日也大量的使用自行车。
>>>weekday_counts.plot(kind='bar')
Matplotlib是一款功能强大的数据可视化工具。它与NumPy的无缝集成,使得Python拥有与MATLAB、R等语言旗鼓相当的能力。
通过使用plot()、bar()、hist()、和pie()等函数,Matplotlib可以方便地绘制散点图、条形图、直方图及饼形图等专业图形。
与Numpy类似,如果我们已经通过Anconada安装了Python,那么就无须再次显式安装Matplotlib了,因为它已经默认被安装了。
如果的确没有安装Matplotlib,可在控制台的命令行使用如下命令在线安装。
1)在Anaconda平台下安装
condainstall matplotlib
2)在Python平台下安装
pip install matplotlib
绘制简单图形
二维图形是人们最常用的图形呈现媒介。通常,我们使用Matplotlib中的子模块pyplot来绘制2D图形。它能让用户较为便捷地将数据图像化,并能提供多样的输出格式。
在使用pyplot模块之前,需要先显示地导入。为了使用方便,我们常为这个模块取一个别名plt。
import numpy as np
from matplotlib import pyplot as plt
x=np.arange(1,11)
y=2*x+5
plt.title("Matplotlibdemo")
plt.xlabel("xaxiscaption")
plt.ylabel("yaxiscaption")
plt.plot(x,y)
plt.show()
以上实例中,np.arange()函数创建x轴上的值。y轴上的对应值存储在另一个数组对象y中。这些点使用matplotlib软件包的pyplot子模块的plot()函数绘制。图形由show()函数显示。
散点图
在可视化图像应用中,散点图的应用范围也很广泛。例如,如果某一个点或某几个点偏离大多数点,成为孤立点,通过散点图就可以一目了然。在机器学习中,散点图常常用在分类、聚类当中,以便显示不同类别。
在Matplotlib中,绘制散点图的方法与使用plt.plot()绘制图形的方式类似。
import matplotlib.pyplotas plt
import numpyas np
#产生50对服从正态分布的样本点
nbPointers= 50
x = np.random.standard_normal(nbPointers)
y = np.random.standard_normal(nbPointers)
# 固定种子数,以便实验结果具有可重复性
np.random.seed(19680801)
colors = np.random.rand(nbPointers)
area = (30 * np.random.rand(nbPointers))**2
plt.scatter(x, y, s = area, c = colors, alpha = 0.5)
plt.show()
运行结果如图所示:
直方图
在数据可视化中,条形图常用来展示和对比可测量数据。pyplot子模块提供bar() 函数来生成条形图。
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']=False
objects=('Python','C++','Java','Perl','Scala','Lisp')
y_pos=np.arange(len(objects))
performance=[10,8,6,4,2,1]
plt.bar(y_pos,performance,align='center',alpha=0.5)
plt.xticks(y_pos,objects)
plt.ylabel('用户量')
plt.title('数据分析程序语言使用分布情况')
plt.show()
以上python代码运行结果如下:
回归分析是一种针对连续型数据进行预测的方法,目的在于分析两个或多个变量之间是否相关以及相关方向和强度的关系。回归分析可以帮助连接有一个自变量变化时因变量的变化。本章介绍不同回归算法及其应用,并使用python来建立回归模型进行分析。几种回归算法分别是线性回归、岭回归和LASSO回归、逻辑回归。
回归分析(regression analysis)是确定两种或两种以上变量间相互依赖定量关系的一种统计分析方法,是应用极其广泛的数据分析方法之一。作为一种预测模型,它基于观测数据建立变量间适当的依赖关系,以分析数据内在规律,并用于预测、控制等问题。
例1:假设我们想要一个能够预测二手车价格的系统。
系统输入:品牌、年龄、发动机性能、里程等我们认为会影响车价的属性信息。
输出:车的价格。
这种输出为数值的问题是回归问题。
现假设x表示车的属性,y表示车的价格。我们通过调查以往的交易情况,能够收集训练数据。而机器学习中的线性回归方法能够构造出一个函数来拟合这些数据,从而得到关于푥的函数푦。如下图所示,其中푤和푤0为待拟合的参数值,拟合函数具有如下表达式:
本章介绍机器学习的基础知识,包括特征工程、高维数据降维、超参数调优,目标是理解并掌握机器学习的主要原理。
数据在真实世界中的表示
在真实的训练数据中总是存在各种各样的问题:存在噪声或者冗余。在这种情况下,需要一种特征降维的方法来减少特征数,减少噪音和冗余,减少过度拟合的可能性
特征维度减少的方法
实现维度减少有两种技术,既特征选择和特征提取
特征选择指的是选择原始数据集中最具代表性或者统计意义的维度特征
特征提取指的是将原始特征转换为一组具有明显物理意义或者统计意义的特征
特征选择
基于统计测试来选择最好的特征,常用的统计指标:
评分最高:SelectKBest
用户指定的最高得分百分比:SelectPercentile
假阳性率(false positive rate):SelectFpr
伪发现率(false discovery rate):SelectFdr
族系误差(family wise error):SelectFwe
特征选择的代码实现
>>> from sklearn.datasetsimport load_iris
>>> from sklearn.feature_selectionimport SelectKBest
>>> from sklearn.feature_selectionimport chi2
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> X_new= SelectKBest(chi2, k=2).fit_transform(X, y)
特征提取
特征提取指的是将原始特征转换为一组具有明显物理意义或者统计意义的特征
提取指的是我们并不会因此丢弃某些维度的数据,而转换指的是保留所有的维度数据,只是将高维数据投影低维数空间
特征提取的转换
思考:考虑三维到二维的转换
特征提取
常用的降维方法:
主成分分析(PCA)
因子分析(Factor Analysis)
PCA算法
主成分分析(PCA)中,数据从原来的坐标系转换到新的坐标系,新坐标系的选择是由数据本身决定的。第一个新坐标轴选择的是原始数据中方差最大的方向,第二个新坐标轴选择和第一个坐标轴正交且具有最大方差的方向。该过程一直重复,重复次数为原始数据中特征的数目
PCA图示
PCA算法过程
线性变换=>新特征轴可由原始特征轴线性变换表征
线性无关=>构建的特征轴是正交的
主要线性分量(或者说是主成分)=>方差最大的方向(希望投影后投影值尽可能分散,而这种分散程度,可以用数学上的方差来表述)
PCA算法的求解就是找到主要线性分量及其表征方式的过程
PCA算法的特点
相应的,PCA解释方差并对离群点很敏感:少量原远离中心的点对方差有很大的影响,从而也对特征向量有很大的影响
PCA的代码实现
>>> from sklearnimport datasets
>>> iris = datasets.load_iris()
>>> data=iris.data
>>> from sklearn.decompositionimport PCA
>>> pca=PCA(n_components=2)
>>> newData=pca.fit_transform(data)
机器学习工作流中最难的部分之一是为模型寻找最佳的超参数。所谓超参数,就是机器学习模型里面的框架参数,比如聚类方法里面类的个数,或者话题模型里面话题的个数等等,都称为超参数。
(1)交叉验证
交叉验证为了让被评估的模型更加准确可信。
不同的训练集、测试集分割的方法将会导致模型的准确率不同,而交叉验证(Cross Validation) 的基本思想是将数据集进行一系列分割,生成多组不同的训练与测试集对,然后分别训练模型并计算测试准确率,最后对结果进行平均处理,从而有效降低测试准确率的差异。
简单交叉验证:将原始数据随机分为两组,一组做为训练集,一组做为验证集,利用训练集训练分类器,然后利用验证集验证模型,记录最后的分类准确率为此分类器的性能指标。好处:处理简单,只需随机把原始数据分为两组即可。坏处:由于是随机的将原始数据分组,所以最后验证集分类准确率的高低与原始数据的分组有很大的关系,得到的结果并不具有说服性。
5折交叉验证:将拿到的数据,分为训练和验证集。以下图7-5为例:将数据分成5份,其中一份作为验证集。然后经过5次(组)的测试,每次都更换不同的验证集。即得到5组模型的结果,取平均值作为最终结果。又称5折交叉验证。
(2)超参数搜索-网格搜索
通常情况下,有很多参数是需要手动指定的(如k-近邻算法中的K值),这种叫超参数。但是手动过程繁杂,所以需要对模型预设几种超参数组合。每组超参数都采用交叉验证来进行评估。最后选出最优参数组合建立模型。
(3)超参数搜索-API
sklearn.model_selection.GridSearchCV(estimator,param_grid=None,cv=None)
estimator:估计器对象
param_grid: 估计器参数(dict或list){“n_neighbors”:[1,3,5]}
cv:指定几折交叉验证
score:准确率
结果分析:best_estimator_:最好的参数模型
best_score_:在交叉验证中测试的最好结果
best_params_: 最好结果的参数设置
cv_results_:每次交叉验证后的测试集准确率结果和训练集准确率结果
K-近邻网格搜索案例
"""
K-近邻算法,网格搜索交叉验证的使用
威斯康星州乳腺癌数据集
含有30个特征、569条记录、目标值为0或1
"""
# 0.导包
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import classification_report
from sklearn.model_selection import GridSearchCV
# 1.导入数据
data_cancer = load_breast_cancer()
# 2.将数据集划分为训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(data_cancer.data, data_cancer.target, test_size=0.25)
# print(x_train)
# 3.数据标准化处理
stdScaler = StandardScaler().fit(x_train)
x_trainStd = stdScaler.transform(x_train)
x_testStd = stdScaler.transform(x_test)
# 4.使用KNeighborsClassifier函数构建
knn模型knn_model = KNeighborsClassifier()
param_grid = {"n_neighbors": [1, 3, 5, 7]}
grid_search = GridSearchCV(knn_model, param_grid=param_grid, cv=5).fit(x_trainStd, y_train)
print("网格搜索中最佳结果的参数设置:", grid_search.best_params_)
print("网格搜索中最高分数估计器的分数为:", grid_search.best_score_)
分类是一种常见的机器学习问题。本章首先讨论分类的基本概念、机器学习中常见的数据集,然后对KNN算法、概率模型、朴素贝叶斯分类、空间向量模型、支持向量机以及集成学习算法和应用等进行介绍。
关联规则是机器学习经典的算法之一。本章首先讨论关联规则的基本概念,然后介绍现实中如何通过关联规则挖掘出数据背后的有用信息。然后讲解典型关联规则算法——Apriori算法的原理和应用。
聚类算法是一种的无监督机器学习算法,在无监督机器学习中占据很大的比例。本章主要介绍几种聚类分析的算法,如划分聚类、层次聚类、密度聚类,以及如何评测聚类效果。
人工神经网络(ANN)是由简单神经元经过相互连接形成网状结构,通过调节各连接的权重值改变连接的强度,进而实现感知判断。神经网络作为一种重要的机器学习方法,已在医学诊断、信用卡欺诈识别、手写数字识别以 及发动机的故障诊断等领域得到了广泛应用。
文本分析是机器学习领域重要的应用之一。通过对文本内部特征提取,获取隐含的语义信息或 概括性主题,从而产生高质量的结构化信息,合理的文本分析技术能够获取作者的真实意图。本章首先讨论文本数据处理的概念,再介绍中英文的文本数据处理方法对比,然后介绍文本分析的案例操作,最后会介绍自然语言处理的应用相关知识。
图像分析是机器学习领域重要的应用之一。本章讨论图像相关的基本概念,然后结合Python图像处理工具包介绍图像数据分析的常用方法。
深度学习是一种利用复杂结构的多个处理层来实现对数据进行高层次抽象的算法,是机器学习的一个重要分支。传统的BP算法往往仅有几层网络,需要手工指定特征且易出现局部最优问题;而深度学习引入了概率生成模型,可自动从训练集里提取特征,解决了手工特征考虑不周的问题;而且初始化了神经网络权重,采用反向传播算法进行训练,与BP 算法相比取得了很好的效果。 本章主要介绍深度学习相关的概念和主流框架,重点介绍卷积神经网络和循环神经网络的理论、整体结构以及常见应用,最后介绍一个基于卷积神经网络识别手写数字实战实验。