Kaggle - Digit Recognizer 手写数字识别 -> KNN 和 fully connected neural network 以及 CNN实现

Competition Add : https://www.kaggle.com/c/digit-recognizer

此题目已经把每个图片向量化了。下载到的data里包含三个文件:

数据文件train.csv和test.csv包含从0到9的手绘数字的灰度图像。

每个图像的高度为28个像素,宽度为28个像素,总共为784个像素。每个像素具有与其相关联的单个像素值,指示该像素的亮度或暗度,较高的数字意味着较暗。该像素值是0到255之间的整数,包括0和255。

训练数据集(train.csv)有785列。第一列称为“标签”,是用户绘制的数字。其余列包含关联图像的像素值。

训练集中的每个像素列都具有像pixelx这样的名称,其中x是0到783之间的整数,包括0和783。为了在图像上定位该像素,假设我们已经将x分解为x = i * 28 + j,其中i和j是0到27之间的整数,包括0和27。然后,pixelx位于28 x 28矩阵的第i行和第j列上(索引为零)。

例如,pixel31表示从左边开始的第四列中的像素,以及从顶部开始的第二行,如下面的ascii图中所示。

在视觉上,如果我们省略“像素”前缀,像素组成图像如下:

000 001 002 003 ... 026 027
028 029 030 031 ... 054 055
056 057 058 059 ... 082 083
 | | | | ...... | |
728 729 730 731 ... 754 755
756 757 758 759 ... 782 783 

测试数据集(test.csv)与训练集相同,只是它不包含“标签”列。

K近邻

具体代码如下:

import numpy as np  # linear algebra
import csv

import operator
import os
import time  # 会用到时间函数
# print(os.listdir("./data"))  # 列出此文件夹下的所有文件目录

"""

train_mat 是训练数据组成的矩阵,即train.csv去掉表头 和 每一行的第一个数(label)之后的剩余部分

"""
def digitRecognizer():
    train_labels = []  # 存储 训练数据的标签
    test_labels = []  # 存储 测试数据的标签
    train_list = []  # 存储 去除表头的所有列表
    test_list = []   # 存储 去除表头的所有测试数据列表
    len_train = 0  # 存储 训练集的个数
    with open('./data/train.csv') as f:
        content = csv.reader(f)
        dot = 0
        for one in content:
            if dot > 0:
                # trans_int = [ int(i) for i in one ] 或者用下面的方法
                trans_int = list(map(int, one))  # 把列表中的str全部转化成int类型
                train_list.append(trans_int)
            dot += 1
        len_train = len(train_list)
    # 因为list不支持list**2操作(幂乘),所以需要转化成numpy矩阵的形式。
    train_mat = np.zeros((len_train, 784))  # numpy.matrix类型,用0填充一个 len_train*784的矩阵
    dot = 0
    for one in train_list:
        train_labels.append(one[0])
        train_mat[dot, :] = one[1:]
        dot += 1
    # print(train_mat.shape) # (42000, 784)

    with open('./data/test.csv') as f:
        content = csv.reader(f)
        dot = 0
        for one in content:
            if dot > 0:
                # trans_int = [ int(i) for i in one ] 或者用下面的方法
                trans_int = list(map(int, one))  # 把列表中的str全部转化成int类型
                test_list.append(trans_int)
            dot += 1
    # print(len(test_list))  # 返回 28000
    dot = 1
    for ele in test_list:
        result = classify(ele, train_mat, train_labels, 5)  # k=5
        test_labels.append(result)

        # print(result)
        dot += 1
        if dot % 1000 == 0:  # 每一千次输出一次时间
            print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))

    # 写ans文件
    with open('./data/ans.csv', 'w', encoding='UTF-8', newline='') as f:
        head = ['ImageId', 'Label']
        ww = csv.writer(f)
        ww.writerow(head)
        for i in range(len(test_labels)):
            mid = [str(i + 1), str(test_labels[i])]
            ww.writerow(mid)
        print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))


def classify(inX, data_set, labels, k):  # 分类函数,k近邻判断结果所属类别
    data_set_size = data_set.shape[0]  # 返回 矩阵行数
    diff_mat = np.tile(inX, (data_set_size, 1)) - data_set  # 计算此测试向量 与 矩阵中每一个行向量中元素的差值
    sq_diff_mat = diff_mat ** 2  # 各个差值求平方
    sq_distance = sq_diff_mat.sum(axis=1)  # 按行向量求和
    distance = sq_distance ** 0.5  # 再开方,此时得到的是此测试向量到各个训练数据的 欧氏距离
    sorted_dis = distance.argsort()  # 将 距离 升序排序, 返回的是对应的index索引号
    class_count = {}
    for i in range(k):
        vote = labels[sorted_dis[i]]
        class_count[vote] = class_count.get(vote, 0) + 1
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
    return sorted_class_count[0][0]
    """
    # 上面sorted函数的第一个参数是可迭代的对象
    # operator.itemgetter(1)是获取可迭代对象中 第一个域的值(从0开始),即对字典的value值排序
    # reverse=True 表示排序完成之后(默认从小到大),再逆序。即由大到小排序
    # 排序完成之后,最大的值在最前面。并且返回的是list
    # 例如:待排序的dist为{'b':111, 'a':666}, 返回的结果是[('a', 666), ('b', 111)],tuple也是可以按下标索引的
    
    """


if __name__ == '__main__':
    digitRecognizer()

可以看到训练集有42000条数据,每条数据有784(28像素*28像素)个数字。测试集有28000条数据,每条也有784个数字。

在进行距离的计算时,需要将测试集里的每一条数据 分别计算到训练集4.2万个‘图’的每一个距离。即算法要为每个测试向量做42000次的距离计算,并且每个距离计算包含784个浮点运算,然后还需要开方,总计要计算28000次(28000个测试数据)。

总计算次数数量级:几乎是千亿级别了。(784*4.2万*2.8万 = 9219.84亿)所以可以看出会非常耗时。

我运行此代码的时候大致输出了一下时间,内存12G, i5CPU 无SSD大概运行了仨小时。-_-#

得到了ans.csv文件

内容形如:

然后满怀期待的进行了提交...:)

额... 可以看到准确率为96.900%

用的k近邻算法,k的值选取为5.

并且我观察了一下,发现向量中大部分为0值,就没进行归一化( normalization )。

不过在两千多组提交中排名一千六百多,这成绩还是很不理想的。

 

Neural Network 

        - 3 level, 500 hidden nodes, 7 epoch, learning rate is 0.1

        - Accuracy  97.371%

import numpy
import csv
import matplotlib.pyplot as plt
import time


# 在训练神经网络的过程中有两个阶段,第一个阶段:计算输出,如同query()所做的事情
# 第二个阶段就是反向转播误差,告知如何优化链接权重
# neural network class definition
class neuralNetwork:
    # initialise the neural network
    def __init__(self, input_n, hidden_n, output_n, learning_rate):
        self.inodes = input_n
        self.hnodes = hidden_n
        self.onodes = output_n

        self.lrate = learning_rate

        # 权重矩阵
        #         self.Wi_h = numpy.random.rand(self.hnodes, self.inodes) - 0.5
        #         self.Wh_o = numpy.random.rand(slef.onodes, self.hnodes) - 0.5

        # 符合正态分布的权重
        # numpy.random.normal(平均值, 标准方差--即节点数目的-0.5次方, numpy数组大小)
        self.Wi_h = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes))
        self.Wh_o = numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes))

    def Sigmod(self, x):
        return 1.0 / (1 + numpy.exp(-x * 1.0))

    # train the neural network
    def train(self, inputs_list, targets_list):
        inputs = numpy.array(inputs_list, ndmin=2).T  # shape 为n*1的列向量
        targets = numpy.array(targets_list, ndmin=2).T

        hidden_inputs = numpy.dot(self.Wi_h, inputs)  # 矩阵点乘
        hidden_outputs = self.Sigmod(hidden_inputs)  # S函数

        final_inputs = numpy.dot(self.Wh_o, hidden_outputs)
        final_outputs = self.Sigmod(final_inputs)  # S函数
        # 输出层误差
        output_errors = targets - final_outputs
        # 隐含层误差计算
        hidden_errors = numpy.dot(self.Wh_o.T, output_errors)  # 去除归一化权重的误差
        # 更新(训练)权重矩阵
        self.Wh_o += self.lrate * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
        self.Wi_h += self.lrate * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))

    # query the neural network
    def query(self, inputs_list):
        inputs = numpy.array(inputs_list, ndmin=2).T  # 维度为2 然后转秩

        hidden_inputs = numpy.dot(self.Wi_h, inputs)  # 矩阵点乘
        hidden_outputs = self.Sigmod(hidden_inputs)  # S函数

        final_inputs = numpy.dot(self.Wh_o, hidden_outputs)
        final_outputs = self.Sigmod(final_inputs)  # S函数

        return final_outputs

    def file_read(self):
        road = 'F:\\KaggleInAction\\Digit_Recognizer\\data\\train.csv'
        data = []
        index = []
        with open(road, encoding='utf-8') as f:
            f.readline()  # 略掉首行
            contents = csv.reader(f)
            for line in contents:
                mid = (numpy.asfarray(line[1:])/255.0*0.99) + 0.01  # 将文本类型数据转换成数值类型 并缩放到0.01-1 之间
                data.append(mid)
                index.append(int(line[0]))
                # print(line)
                # # numpy.asfarray() 将str类型的数据 转成 数值类型的数据
                # image_array = numpy.asfarray(line[1:]).reshape((28, 28))
                # # cmap='Greys' 是灰度调色板
                # plt.imshow(image_array, cmap='Greys', interpolation='None')
                # plt.show()
        return data, index


if __name__ == '__main__':
    print('Begin: ', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))  # 开始时刻
    input_nodes = 784  # 输入层节点784个 即输入的数据是一个 784*1 的向量
    hidden_nodes = 500  # 设置隐含层节点 500个
    output_nodes = 10  # 输出层节点 10个

    learning_rate = 0.1

    n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

    for epoch in range(7):  # 权重只初始化一次,将数据集多次训练(此处是7次), 但注意:训练次数过多会导致过拟合
        data, key = n.file_read()
        target_list = []
        for num in key:
            matrix = numpy.zeros(output_nodes) + 0.01  # 创建每个训练数据的目标输出
            matrix[num] = 0.99
            target_list.append(matrix)
        for i in range(len(data)):
            n.train(data[i], target_list[i])  # 训练权重矩阵(训练神经网络)
        print('epoch', epoch)  # 输出epoch次数

    # way = './mnist_test.csv'
    way = 'F:\\KaggleInAction\\Digit_Recognizer\\data\\test.csv'
    test = []
    # ans = []
    res = []
    cnt = 0
    with open(way, encoding='utf-8') as f:
        f.readline()  # 略掉首行
        for line in f:
            ss = line.strip('\n').split(',')
            # ans.append(int(ss[0]))
            test = numpy.asfarray(ss)
            mid = n.query((test/255.0*0.99) + 0.01)
            label = numpy.argmax(mid)
            res.append(label)
    # for i in range(len(ans)):
    #     if ans[i] == res[i]:
    #         cnt += 1
    with open('F:\\KaggleInAction\\Digit_Recognizer\\data\\neural_network_ans.csv', 'w', encoding='utf-8') as f:
        f.write('ImageId,Label' + '\n')
        for i in range(len(res)):
            f.write(str(i+1) + ',' + str(res[i]) + '\n')
    print('Done... ', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))  # 运行结束时刻
    # print('performance = ', cnt*1.0/len(ans))

运行时间大概半个小时

排名还是那么靠后... /手动捂脸

 

CNN

epoch=5 batch_size=64 optimizer='rmsprop' activation='relu' last_activation='softmax'

from keras import models
from keras import layers
import numpy as np
import pandas as pd
import time

print('Begin...', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))  # 开始时间

# 换成你的文件路径
img_train = pd.read_csv('F:/KaggleInAction/Digit_Recognizer/data/train.csv')
img_test = pd.read_csv('F:/KaggleInAction/Digit_Recognizer/data/test.csv')

# 将原始数据转换为图片格式,利用CNN,图片格式(m, n_H, n_W, n_C)
img_train = np.array(img_train)
img_test = np.array(img_test)

train_images = np.zeros((img_train.shape[0], 28, 28, 1))
train_labels = np.zeros(img_train.shape[0])
test_images = np.zeros((img_test.shape[0], 28, 28, 1))
test_labels = np.zeros(img_test.shape[0])

for i in range(img_train.shape[0]):
    train_images[i] = img_train[i][1:].reshape(28, 28, 1)
    train_labels[i] = img_train[i][0].astype(int)
for i in range(img_test.shape[0]):
    test_images[i] = img_test[i].reshape(28, 28, 1)

# Normalization
train_images = train_images.astype('float32') / 255
test_images = test_images.astype('float32') / 255


# 将整数序列编码为二进制矩阵
def vectorize_sequences(sequences, dimension=10):
    results = np.zeros((len(sequences), dimension), dtype='float32')
    for i, sequence in enumerate(sequences):
        results[i, int(sequence)] = 1.
    return results


train_labels = vectorize_sequences(train_labels)  # (42000,) -> (42000, 10)
test_labels = vectorize_sequences(test_labels)  # (28000,) -> (28000, 10)

print(train_images.shape)  # (42000, 28, 28, 1)
print(train_labels.shape)  # (42000, 10)
print(test_images.shape)  # (28000, 28, 28, 1)
print(test_labels.shape)  # (28000, 10) 目前全0


# 模型
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))

# 3D 转成 1D
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))

model.compile(optimizer='rmsprop', loss='categorical_crossentropy', metrics=['accuracy'])
history = model.fit(train_images, train_labels, epochs=5, batch_size=64)
# print(history.history.keys())
# test_loss, test_acc = model.evaluate(test_images, test_labels)
# print(test_loss)
# print(test_acc)
pred = model.predict(test_images, batch_size=256)  # 预测结果
print(pred.shape)
pred_X = np.argmax(pred, axis=1)
# print(pred_X)  # [2 0 9 ... 3 9 2] 就是那2800个测试数据的标签

# 写文件 # 换成你的文件路径
result = pd.DataFrame({'ImageId': np.arange(1, 28001), 'Label': pred_X})
result.to_csv("F:/KaggleInAction/Digit_Recognizer/cnn_ans.csv", index=False)

print('End...', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))  # 结束时间

大约运行了2分钟

然后迫不及待的summit 

Accuracy 为 98.828% 终于杀进前 50%了.... -_-#

后续会给出别的解法。

有问题请留言,欢迎转发,转发需标明出处。

 

稍后补充更新...

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值