三个csv文件,分别是提交样例、测试集、训练集
train.csv是训练样本集,大小42001*785,第一行是文字描述,所以实际的样本数据大小是42000*785,其中第一列的每一个数字是它对应行的label,可以将第一列单独取出来,得到42000*1的向量trainLabel,剩下的就是42000*784的特征向量集trainData,所以从train.csv可以获取两个矩阵trainLabel、trainData。
下面给出代码,另外关于如何从csv文件中读取数据,参阅:Python csv模块的使用
def loadTrainData():
l=[]
with open('train.csv') as file:
lines=csv.reader(file)
for line in lines:
l.append(line) #42001*785,append()函数把每一行line添加到l的末尾
l.remove(l[0]) #去掉第一行
l=array(l) #把l转为动态数组
label=l[:,0] #标签是第0列
data=l[:,1:] #数据是第1列到最后一列
return nomalizing(toInt(data)),toInt(label) #把data正则化
def toInt(array):
array=mat(array) #把数组转成矩阵,用来求m、n,方便进行for循环遍历
m,n=shape(array) #m,n是行数和列数
newArray=zeros((m,n))
for i in range(m):
for j in range(n):
newArray[i,j]=int(array[i,j])
return newArray
array和mat的区别:Python mat与array的区别
nomalizing()函数做的工作是归一化,因为train.csv里面提供的表示图像的数据是0~255的,为了简化运算,我们可以将其转化为二值图像,因此将所有非0的数字,即1~255都归一化为1。nomalizing()函数如下:
def nomalizing(array):
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。
def loadTestData():
l=[]
with open('test.csv') as file:
lines=csv.reader(file)
for line in lines:
l.append(line)
#28001*784
l.remove(l[0])
data=array(l)
return nomalizing(toInt(data))
这个文件里的数据是28001*2,第一行是文字说明,可以去掉,第一列表示图片序号1~28000,第二列是图片对应的数字。从knn_benchmark.csv可以得到28000*1的测试结果矩阵testResult,代码:
def loadTestResult():
l=[]
with open('knn_benchmark.csv') as file:
lines=csv.reader(file)
for line in lines:
l.append(line)
#28001*2
l.remove(l[0])
label=array(l)
return toInt(label[:,1])
分类主体程序,计算欧式距离,选择距离最小的k个,返回k个中出现频率最高的类别
原理:https://blog.csdn.net/u012162613/article/details/41768407
#inX是所要测试的向量 1*784
#dataSet是训练样本集,一行对应一个样本。 42000*784
#dataSet对应的标签向量为labels
#k是所选的最近邻数目
def classify(inX, dataSet, labels, k):
inX=mat(inX)
dataSet=mat(dataSet)
labels=mat(labels)
#把inX、dataSet、labels都转化为矩阵
dataSetSize = dataSet.shape[0] #shape[0]得出dataSet的行数,即样本个数,42000
diffMat = tile(inX, (dataSetSize,1)) - dataSet
#tile(A,(m,n))将数组A作为元素构造m行n列的数组,diffMat得到了目标与训练数值之间的差值。
sqDiffMat = array(diffMat)**2
sqDistances = sqDiffMat.sum(axis=1) #array.sum(axis=1)按行累加,axis=0为按列累加
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort() #sortedDistIndicies[0]表示排序后排在第一个的那个数在原来数组中的下标
classCount={}
for i in range(k):
voteIlabel = labels[0,sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #对各个label计数累加
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
tile这个函数非常牛逼啊,我只说它在这个里面是什么意思,我们知道inX是个向量,而dataset是个矩阵,两者之间要进行相减的运算,需要把这个向量也补成一个和dataset有相同行数列数的矩阵,怎么个补法呢。这就要看tile()的第二个参数了,也就是上面的(datasetsize,1),这个参数的意思就是把inX补成有datasetsize行数的矩阵。
假如inX是(1,2) datasetsize =3 那么经过tile()转换后产生了一个这样的矩阵([1,2],[1,2],[1,2]),然后和dataset相减就是根据矩阵的减法进行的
#get(key,x)从字典中获取key对应的value,没有key的话返回0。
对于测试集里28000个样本,调用28000次这个函数即可。
classify0函数详解:knn算法 -classify0函数详解
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)
def handwritingClassTest():
trainData,trainLabel=loadTrainData()
testData=loadTestData()
testLabel=loadTestResult()
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, the real answer is: %d" % (classifierResult, testLabel[0,i]))
if (classifierResult != testLabel[0,i]): errorCount += 1.0
print("\nthe total number of errors is: %d" % errorCount)
print("\nthe total error rate is: %f" % (errorCount/float(m)))
saveResult(resultList)
handwritingClassTest();