train.csv是训练样本集,大小42001*785,第一行是文字描述,所以实际的样本数据大小是42000*785,其中第一列的每一个数字是它对应行的label,可以将第一列单独取出来,得到42000*1的向量trainLabel,剩下的就是42000*784的特征向量集trainData,所以从train.csv可以获取两个矩阵trainLabel、trainData。
def loadTrainData():
l=[]
with open('train.csv') as file:
lines=csv.reader(file)
for line in lines:
#把数据一行行存入列表
l.append(line) #42001*785
#删除第一行文字描述( 42000*785)
l.remove(l[0])
#使用numpy包中的array()函数把列表l转换为数组
l=array(l)
#截取数组的第一列,即标签向量列(42000*1)
label=l[:,0]
#截取数组除第一列外所有数据(42000*784)
data=l[:,1:]
return nomalizing(toInt(data)),toInt(label)
这里还有两个函数需要说明一下,toInt()函数,是将字符串转换为整数,因为从csv文件读取出来的,是字符串类型的,比如‘253’,而我们接下来运算需要的是整数类型的,因此要转换,int(‘253’)=253。toInt()函数如下:
def toInt(array):
#调用numpy中mat()函数将数组转换为矩阵,然后可以对矩阵进行一些线性代数的操作。
array=mat(array)
#调用numpy中的shape()函数求出矩阵的维度
m,n=shape(array)
#调用numpy中的zeros()函数生成一个和所求矩阵同维度(m,n)大小的以 0 填充的矩阵
newArray=zeros((m,n))
for i in range(m):
for j in range(n):
newArray[i,j]=int(array[i,j])
return newArray
nomalizing()函数做的工作是归一化,因为train.csv里面提供的表示图像的数据是0~255的,为了简化运算,我们可以将其转化为二值图像,因此将所有非0的数字,即1~255都归一化为1。nomalizing()函数如下:
def nomalizing(array):
#调用numpy中的shape()函数求出矩阵的维度
m,n=shape(array)
for i in range(m):
for j in range(n):
if array[i,j]!=0:
array[i,j]=1
return array
test.csv里的数据大小是28001*784,第一行是文字描述,因此实际的测试数据样本是28000*784,与train.csv不同,没有label,28000*784即28000个测试样本,我们要做的工作就是为这28000个测试样本找出正确的label。所以从test.csv我们可以得到测试样本集testData,代码如下:
def loadTestData():
l=[]
with open('test.csv') as file:
lines=csv.reader(file)
for line in lines:
l.append(line)
#28001*784 -----> 28000*784
l.remove(l[0])
data=array(l)
return nomalizing(toInt(data))
到这里,数据分析和处理已经完成,我们获得的矩阵有:trainData、trainLabel、testData
这里我们采用kNN算法来分类,核心代码:
def classify(inX, dataSet, labels, k):
#待分类的输入向量inX,输入的训练样本集dataset,标签向量labels, k表示用于选择最近邻居的数目
#调用numpy中mat()函数将输入向量inX转换为矩阵,然后可以对矩阵进行一些线性代数的操作。
inX=mat(inX)
dataSet=mat(dataSet)
labels=mat(labels)
#获取训练样本集dataSet的行数
dataSetSize = dataSet.shape[0]
#先把标签向量重复dataSet行 1列生成一个和dataSet同维度的矩阵再减去dataSet
diffMat = tile(inX, (dataSetSize,1)) - dataSet
sqDiffMat = array(diffMat)**2
#将矩阵每一行里面的所有元素相加形成一个dataSize行 1列的矩阵
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
#将所求距调用numpy中的argsort()函数离排序
#argsort()函数是将x中的元素从小到大排列,提取其对应的index(索引号)
sortedDistIndicies = distances.argsort()
classCount={}
for i in range(k):
voteIlabel = labels[0,sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
#函数 sorted 方法返回的是一个新的 list
# key=operator.itemgetter(1), reverse=True 以第二个值【itemgetter(1)】降序排列
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
简单说明一下,inX就是输入的单个样本,是一个特征向量。dataSet是训练样本,对应上面的trainData,labels对应trainLabel,k是knn算法选定的k,一般选择0~20之间的数字。这个函数将返回inX的label,即图片inX对应的数字。
对于测试集里28000个样本,调用28000次这个函数即可。
kaggle上要求提交的文件格式是csv,上面我们得到了28000个测试样本的label,必须将其保存成csv格式文件才可以提交
代码:
def saveResult(result):
with open('result.csv','w',newline = '') as myFile:
myWriter=csv.writer(myFile)
for i in result:
tmp = []
tmp.append(i)
myWriter.writerow(tmp)
上面各个函数已经做完了所有需要做的工作,现在需要写一个函数将它们组合起来解决digit recognition这个题目。我们写一个handwritingClassTest函数,运行这个函数,就可以得到训练结果result.csv。
def handwritingClassTest():
trainData,trainLabel=loadTrainData()
testData=loadTestData()
m,n=shape(testData)
errorCount=0
resultList=[]
for i in range(m):
classifierResult = classify(testData[i], trainData, trainLabel, 5)
resultList.append(classifierResult)
print ("the classifier came back with: %d" % classifierResult)
saveResult(resultList)
7.kaggle 提交结果
8.参考文献(刚开始学机器学习,第一次做kaggle上的题,所以这篇博客基本上基本上是翻译下面这篇博客)
参考博客:https://blog.csdn.net/u012162613/article/details/41929171