机器学习基础
机器学习是利用数据,从数据中归纳出规律,并用来对新事物进行预测。所以,机器学习首先要有数据,假设我们收集了一份关于西瓜的数据:
现实世界的任何事物其实都可以通过属性或着特征来进行描述,上图给出的就是通过三个属性来描述西瓜的一组数据。属性的数目我们称之为维数,本例中表示西瓜用了三个特征,因此就是三维。 下面的图表示样本空间(sample space)或者属性空间(attribute space),我们也可以看到这是一个三维空间。
我们把数据中的每一行称为一个示例或样本;
反映事件或对象在某方面的表现或性质的事项,如:色泽、根蒂、敲声,称为属性或特征;
属性上的取值,例如:青绿、乌黑。称为属性值或特征值;
我们把一个示例(样本)称为一个特征向量。
一般地,令 D={x1,x2,..,x**m} 表示包含 m 个示例的数据集,每个示例由 d 个属性描述(例如上面的西瓜数据使用了三个属性),则每个示例:
x**i=(x**i1;x**i2;...;xid)
是 d 维样本空间X中的一个向量,x**i∈X,其中xij是x**i在第 j 个属性上的取值。d 称为样本x**i的维数。
从数据中学得模型的过程称为“学习”或“训练”,这个过程通过执行某个学习算法来完成。训练过程中使用的数据称为“训练数据”,其中每个样本称为一个“训练样本”,训练样本组成的集合称为“训练集”,学习过程就是为了找出或逼近真相。
假设空间
假设空间在已知属性和属性可能取值的情况下,对所有可能满足目标的情况的一种毫无遗漏的假设集合。 每种特征的组合都认为是一个假设(hypothesis),如,(色泽=青绿;根蒂=蜷缩;敲声=浊响)是一种假设,所有假设的集合我们称之为假设空间。 如果“色泽”,“根蒂”,“敲声”分别有3,2,2种可能,(每种特征值都要加一种任意值可能)那么假设空间的规模就是4x3x3 + 1 = 37。
从这幅图可以看出,每种特征值在计算可能性的时候都加了一种可能,就是任意值可能,我们用“*”表示,最后结果加1是由于存在一种可能就是根本没有“好瓜”这个概念,或者说“好瓜”跟这些特征都没有关系。当给定一个训练集进行训练的时候,模型会逐渐删除那些与正例不一致的假设和(或)与反例一致的假设,最后获得与训练集一致的假设。而剩下的这些假设可能有多个,我们把剩下的这些假设的集合称之为“版本空间”(version space)。
归纳偏好
在上文中我们可以发现版本空间中可能有多个假设,究竟选择什么样的假设作为模型的基础呢?比如是越特殊越好,还是越泛化越好呢?比如倾向于色泽还是让敲声有更多的权重?这些针对某种类型的假设的偏好,就叫做“归纳偏好”或者简称“偏好”。而具体到实际的问题解决,往往就是这个偏好的设置最为关键,直接决定了算法的效果。
分类
分类是机器学习的一项主要任务,主要是将实例数据划分到合适的分类中。
回归
机器学习的另外一项任务是回归,主要是预测数值型的数据,比如通过数据值拟合曲线等。 大多数人可能都见过回归的例子——数据拟合曲线:通过给定数据点的最优拟合曲线。
机器学习的分类
分类和回归属于监督学习,之所以称之为监督学习,是因为这类算法必须知道预测什么,即目标变量的分类信息。 与监督学习相对应的是无监督学习,此时数据没有类别信息,也不会给定目标值。在无监督学习中,将数据集合分成由类似的对象组成的多个类的过程被称为聚类;将寻找描述数据统计值的过程称之为密度估计。此外,无监督学习还可以减少数据特征的维度,以便我们可以使用二维或三维图形更加直观地展示数据信息。
下表列出了机器学习的主要任务,以及解决相应问题的算法。
朴素贝叶斯算法属于有/无监督学习的回归/分类算法?
A、监督学习;分类
B、监督学习;回归
C、无监督学习;分类
D、无监督学习;回归
k-近邻算法
k近邻算法概述
KNN算法的编写以及测试
它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。
一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。 最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。 现在我们得到了样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到k个距离最近的电影。假定k=3,则三个最靠近的电影依次是He's Not Really into Dudes
,Beautiful Woman
,California Man
。k-近邻算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。
该函数的功能是使用&-近邻算法将每组数据划分到某个类中,其伪代码如下: 对未知类别属性的数据集中的每个点依次执行以下操作: (1)计算已知类别数据集中的点与当前点之间的距离;
(2)按照距离递增次序排序;
(3)选取与当前点距离最小的走个点;
(4)确定前k个点所在类别的出现频率;
(5)返回前k个点出现频率最高的类别作为当前点的预测分类。
classifyO ()
函数有4个输入参数:用于分类的输入向量是inX
,输入的训练样本集为dataset
,标签向量为labels
,最后的参数k表示用于选择最近邻居的数目,其中标签向量的元素数目和矩 阵dataset
的行数相同。使用欧氏距离公式,计算两个向量点“和成之间的距离 ,计算完所有点之间的距离后,可以对数据按照从小到大的次序排序。然后,确定前k个距离最小元素所在的主要分类 , 输入k总是正整数;最后,将classCount
字典分解为元组列表,然后 使用程序第二行导入运算符模块的itemgetter
,按照第二个元素的次序对元组进行排序。此处的排序为逆序,即按照从最大到最小次序排序,最后返回发生频率最高的元素标签。
代码如下:
# 函数说明:kNN算法,分类器
def classify0(inX, dataSet, labels, k):
#numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
#二维特征相减后平方
sqDiffMat = diffMat**2
#sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
#开方,计算出距离
distances = sqDistances**0.5
#返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
#定一个记录类别次数的字典
classCount = {}
for i in range(k):
#取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值进行排序
#key=operator.itemgetter(0)根据字典的键进行排序
#reverse降序排序字典
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
import numpy as np
import operator
"""
Parameters:
无
Returns:
group - 数据集
labels - 分类标签
"""
# 函数说明:创建数据集
def createDataSet():
#六组二维特征
group = np.array([[3,104],[2,100],[1,81],[101,10],[99,5],[98,2]])
#六组特征的标签
labels = ['爱情片','爱情片','爱情片','动作片','动作片','动作片']
return group, labels
# 函数说明:kNN算法,分类器
def classify0(inX, dataSet, labels, k):
#numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
#二维特征相减后平方
sqDiffMat = diffMat**2
#sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
#开方,计算出距离
distances = sqDistances**0.5
#返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
#定一个记录类别次数的字典
classCount = {}
for i in range(k):
#取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值进行排序
#key=operator.itemgetter(0)根据字典的键进行排序
#reverse降序排序字典
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
if __name__ == '__main__':
###########
#创建数据集
group,labels=createDataSet()
test=[101,20]
# KNN分类
# 打印分类结果
print(classify0(test,group,labels,0))#这里的k传1-6均可,因为group的长度为6,而k是正整数
############
从文本中解析数据
这些数据存放在文本文件datingTestSet中,每个样本数据占据一行,总共有1000行。样本主要包括以下3种特征: 每年获得的飞行常客里程数 玩视频游戏所耗时间百分比 每周消费的冰淇淋公升数 在将上述特征数据输人到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式。创建名为file2matrix的函数,以此来处理输入格式问题。该函数的输入为文件名字符串,输出为训练样本矩阵和类标签向量。
import numpy as np
"""
Parameters:
filename - 文件名
Returns:
returnMat - 特征矩阵
classLabelVector - 分类Label向量
"""
# 函数说明:打开并解析文件,对数据进行分类:1代表不喜欢,2代表魅力一般,3代表极具魅力
def file2matrix(filename):
#打开文件
fr = open(filename)
#读取文件所有内容
arrayOLines = fr.readlines()
#得到文件行数
numberOfLines = len(arrayOLines)
#返回的NumPy矩阵,解析完成的数据:numberOfLines行,3列
returnMat = np.zeros((numberOfLines,3))
#返回的分类标签向量
classLabelVector = []
#行的索引值
index = 0
for line in arrayOLines:
#s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
line = line.strip()
#使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片。
listFromLine = line.split('\t')
#将数据前三列提取出来,存放到returnMat的NumPy矩阵中,也就是特征矩阵
returnMat[index,:] = listFromLine[0:3]
#根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
if listFromLine[-1] == 'didntLike':
classLabelVector.append(1)
elif listFromLine[-1] == 'smallDoses':
classLabelVector.append(2)
elif listFromLine[-1] == 'largeDoses':
classLabelVector.append(3)
index += 1
return returnMat, classLabelVector
if __name__ == '__main__':
#打开的文件名
filename = "/data/shixunfiles/9332616af5f2c1fe9beb48430afe2571_1603263995399.txt"
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
print(datingDataMat)
print(datingLabels)
从上面的代码可以看到,python处理文本文件非常容易。首先我们需要知道文本文件包含多少行。打开文件,得到文件的行数 。然后创建以零填充的矩阵Numpy(实际上,NumPy是一个二维数组,这里暂时不用考虑其用途)。为了简化处理,我们将该矩阵的另一维度设置为固定值, 你可以按照自己的实际需求增加相应的代码以适应变化的输入值。循环处理文件中的每行数据 , 首先使用函数line.strip()截取掉所有的回车字符,然后使用tab字符\t将上一步得到的整行数据分割成一个元素列表。接着,我们选取前3个元素,将它们存储到特征矩阵中。python语言可以使用索引值-1表示列表中的最后一列元素,利用这种负索引,我们可以很方便地将列表的最后一列存储到向量classLabelVector中。需要注意的是,我们必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则python语言会将这些元素当作字符串处理。以前我们必须 自己处理这些变量值类型问题,现在这些细节问题完全可以交给NumPy函数库来处理。
import numpy as np
"""
Parameters:
filename - 文件名
Returns:
returnMat - 特征矩阵
classLabelVector - 分类Label向量
"""
# 函数说明:打开并解析文件,对数据进行分类:1代表不喜欢,2代表魅力一般,3代表极具魅力
def file2matrix(filename):
#打开文件
fr = open(filename)
#读取文件所有内容
arrayOLines = fr.readlines()
#得到文件行数
numberOfLines = len(arrayOLines)
#返回的NumPy矩阵,解析完成的数据:numberOfLines行,3列
returnMat = np.zeros((numberOfLines,3))
#返回的分类标签向量
classLabelVector = []
#行的索引值
index = 0
for line in arrayOLines:
#s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
line = line.strip()
#使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片。
listFromLine = line.split('\t')
#将数据前三列提取出来,存放到returnMat的NumPy矩阵中,也就是特征矩阵
returnMat[index,:] = listFromLine[0:3]
#根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
if listFromLine[-1] == 'didntLike':
classLabelVector.append(1)
elif listFromLine[-1] == 'smallDoses':
classLabelVector.append(2)
elif listFromLine[-1] == 'largeDoses':
classLabelVector.append(3)
index += 1
return returnMat, classLabelVector
if __name__ == '__main__':
#打开的文件名
filename = "datingTestSet.txt"
#打开并处理数据
############
#请在此处添加你的代码
datingDataMat,datingLabels=file2matrix(filename)
###########
print(datingDataMat)
print(datingLabels[0:20])
本关任务:某人使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选,但她没有从中找到喜欢的人。经过一番总结,她发现曾交往过三种类型的人: 不喜欢的人 魅力一般的人 极具魅力的人 她觉得可以在周一到周五约会那些魅力一般的人,而周末则更喜欢与那些极具魅力的人为伴。她希望我们的分类软件可以更好地帮助她将匹配对象划分到确切的分类中。 影响分类的特征应该是同等重要的,在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值: newValue = {oldValue - min ) / (max-min)
其中max
和min
分别是数据集中的最小特征值和最大特征值。虽然改变数值取值范围增加了分类器的复杂度,但为了得到准确结果,我们必须这样做。 我们可以给这个人一个小程序,通过该程序海伦会在约会网站上找到某个人并输入他的信息。程序会给出她对对方喜欢程度的预测值。
根据提示,在右侧编辑器补充代码,计算并输出数组的平均值和最大值
import numpy as np
import operator
"""
Parameters:
inX - 用于分类的数据(测试集)
dataSet - 用于训练的数据(训练集)
labes - 分类标签
k - kNN算法参数,选择距离最小的k个点
Returns:
sortedClassCount[0][0] - 分类结果
"""
# 函数说明:kNN算法,分类器
def classify0(inX, dataSet, labels, k):
#numpy函数shape[0]返回dataSet的行数
dataSetSize = dataSet.shape[0]
#在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
#二维特征相减后平方
sqDiffMat = diffMat**2
#sum()所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
#开方,计算出距离
distances = sqDistances**0.5
#返回distances中元素从小到大排序后的索引值
sortedDistIndices = distances.argsort()
#定一个记录类别次数的字典
classCount = {}
for i in range(k):
#取出前k个元素的类别
voteIlabel = labels[sortedDistIndices[i]]
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值进行排序
#key=operator.itemgetter(0)根据字典的键进行排序
#reverse降序排序字典
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回次数最多的类别,即所要分类的类别
return sortedClassCount[0][0]
"""
Parameters:
filename - 文件名
Returns:
returnMat - 特征矩阵
classLabelVector - 分类Label向量
"""
# 函数说明:打开并解析文件,对数据进行分类:1代表不喜欢,2代表魅力一般,3代表极具魅力
def file2matrix(filename):
#打开文件
fr = open(filename)
#读取文件所有内容
arrayOLines = fr.readlines()
#得到文件行数
numberOfLines = len(arrayOLines)
#返回的NumPy矩阵,解析完成的数据:numberOfLines行,3列
returnMat = np.zeros((numberOfLines,3))
#返回的分类标签向量
classLabelVector = []
#行的索引值
index = 0
for line in arrayOLines:
#s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
line = line.strip()
#使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片。
listFromLine = line.split('\t')
#将数据前三列提取出来,存放到returnMat的NumPy矩阵中,也就是特征矩阵
returnMat[index,:] = listFromLine[0:3]
#根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
if listFromLine[-1] == 'didntLike':
classLabelVector.append(1)
elif listFromLine[-1] == 'smallDoses':
classLabelVector.append(2)
elif listFromLine[-1] == 'largeDoses':
classLabelVector.append(3)
index += 1
return returnMat, classLabelVector
"""
Parameters:
dataSet - 特征矩阵
Returns:
normDataSet - 归一化后的特征矩阵
ranges - 数据范围
minVals - 数据最小值
"""
# 函数说明:对数据进行归一化
def autoNorm(dataSet):
#获得数据的最小值
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
#最大值和最小值的范围
ranges = maxVals - minVals
#shape(dataSet)返回dataSet的矩阵行列数
normDataSet = np.zeros(np.shape(dataSet))
#返回dataSet的行数
m = dataSet.shape[0]
#原始值减去最小值
normDataSet = dataSet - np.tile(minVals, (m, 1))
#除以最大和最小值的差,得到归一化数据
normDataSet = normDataSet / np.tile(ranges, (m, 1))
#返回归一化数据结果,数据范围,最小值
return normDataSet, ranges, minVals
# 函数说明:通过输入一个人的三维特征,进行分类输出
def classifyPerson():
#输出结果
resultList = ['讨厌','有些喜欢','非常喜欢']
#三维特征用户输入
################
#请在此处添加你的代码
precentTats = 20
ffMiles = 3000
iceCream = 1
###############
#打开的文件名
filename = "datingTestSet.txt"
#打开并处理数据
datingDataMat, datingLabels = file2matrix(filename)
#训练集归一化
normMat, ranges, minVals = autoNorm(datingDataMat)
#生成NumPy数组,测试集
inArr = np.array([precentTats, ffMiles, iceCream])
#测试集归一化
norminArr = (inArr - minVals) / ranges
#返回分类结果
classifierResult = classify0(norminArr, normMat, datingLabels, 3)
#打印结果
print("你可能%s这个人" % (resultList[classifierResult-1]))
if __name__ == '__main__':
classifyPerson()
实践链接机器学习实战 (educoder.net)