机器学习作业1 - 对率回归(逻辑回归)

使用10折交叉验证法和留一法评测对率回归分类器

标题有点长哈……这是第一次作业,来自周志华《机器学习》作业3.4,题目如下:

选择两个UCI数据集,比较10折交叉验证法和留一法所估计出的对率回归(逻辑回归)的错误率。

那么,首先下载数据。实验中我使用的是 IrisWine 这两个数据集,前者简单点,后者复杂点。要注意的是,这两个数据集都是3个分类,为了偷个懒,我就把第三个分类删了,保留前两个分类。

分类器

对率回归分类器还是很简单的,大体思路如下:

假设输入数据维度 m,我们保存一个权重向量,在计算的时候直接计算 m 和权重向量的点积,然后将点积结果加上偏置值,最后将计算结果通过 Sigmoid 函数,并输出预测结果。

为了方便起见,可以把偏置值一起放到权重向量中去,此时只要在数据中加上常数1即可,设加上常数以后的数据维度为 m + 1,则权重向量维度也为 m + 1。接下来给出损失函数如下。关于该函数的推导过程,斯坦福大学机器学习课程–逻辑回归算法 这篇博文已经写的非常详细了,大家可以直接看看。

L(θ)=1mi=1m[yilogh0(xi)+(1yi)log(1h0(xi))]

那么,现在的目标是使得损失函数最小,因为损失函数越小代表着我们模型的预测结果越准确。接下来使用梯度下降法,基本思想是:在损失函数的结果中,对权重向量的每个元素求偏导,并按照比例(学习速率)对权重进行修改。这样一来,权重将向着减少损失函数值的方向发展。具体的推导过程同样可见上面那篇博文。分类器代码如下:

import numpy as np

class Classifier(object):
    def __init__(self, attr_count, learn_rate=0.05):
        self.__attr_count__ = attr_count
        self.__learn_rate__ = learn_rate
        self.__weight__ = np.zeros(shape=[attr_count + 1], dtype=np.float32)

    def fit(self, value, label):
        value = np.append(value, [1.0])
        linear_result = np.dot(value, self.__weight__)
        sigmoid_result = 1.0 / (np.exp(-linear_result) + 1.0)
        for idx in range(self.__attr_count__ + 1):
            update_val = (sigmoid_result - label) * value[idx]
            self.__weight__[idx] -= self.__learn_rate__ * update_val

    def classify(self, value):
        value = np.append(value, [1.0])
        linear_result = np.dot(value, self.__weight__)
        if (1.0 / (np.exp(-linear_result) + 1.0)) > 0.5:
            return 1
        else:
            return 0

准备数据

训练数据的准备很简单,直接使用 Python 的文件操作,读入数据文件即可。其中涉及到一些数组的切片操作,为了提高学习效率,在训练之前使用 numpy 打乱了数据集。

if __name__ == '__main__':
    # 读入并打乱 Wine 数据集
    data_str = open('Data/wine.data').readlines()
    np.random.shuffle(data_str)
    # 使用数组切片操作,分离数据和标签
    wine_value = np.ndarray([len(data_str), 13], np.float32)
    wine_label = np.ndarray([len(data_str)], np.int32)
    for outer_idx in range(len(data_str)):
        data = data_str[outer_idx].strip('\n').split(',')
        wine_value[outer_idx] = data[1:]
        wine_label[outer_idx] = data[0]
    # 进行训练和测试
    test_main(wine_value, wine_label, 13)

训练和验证

因为之前的分类器已经提供好了训练和测试的函数,所以直接调用即可。接下来使用10折交叉验证法和留一法进行验证。
10折交叉验证法:将数据等分为10份,每次用其中9份训练,剩下1份测试,然后统计10次测试的评价错误率。
留一法:将数据分为 k 份(k为数据大小),每次用 k - 1 条数据训练,用1条测试,统计总体错误率。
使用简单的循环语句即可实现上述方法:

def test_main(value, label, attr_count):
    batch_size = len(value) // 10
    total_correct_times = 0
    for idx in range(10):
        print('10折交叉验证:当前第 %d 次' % (idx + 1))
        correct_times = 0
        classifier = LinearClassifier.Classifier(attr_count)
        value_train = np.append(value[0:idx * batch_size], value[(idx + 1) * batch_size:], axis=0)
        label_train = np.append(label[0:idx * batch_size], label[(idx + 1) * batch_size:], axis=0)
        value_test = value[idx * batch_size:(idx + 1) * batch_size]
        label_test = label[idx * batch_size:(idx + 1) * batch_size]
        for repeat in range(TRAIN_TIMES):
            for sub_idx in range(len(value_train)):
                classifier.fit(value_train[sub_idx], label_train[sub_idx])
        for sub_idx in range(len(value_test)):
            result = classifier.classify(value_test[sub_idx])
            if result == label_test[sub_idx]:
                correct_times += 1
        total_correct_times += correct_times
        print('准确率:%.2f%%\n' % (correct_times * 100 / len(value_test)))
    print('10折交叉验证结束,平均准确率:%.2f%%\n' % (total_correct_times * 100 / len(value)))
    total_times = len(value)
    correct_times = 0
    for idx in range(total_times):
        print('留一法第 %d 次,共 %d 次' % (idx, total_times))
        classifier = LinearClassifier.Classifier(attr_count)
        value_train = np.append(value[0:idx], value[(idx + 1):], axis=0)
        label_train = np.append(label[0:idx], label[(idx + 1):], axis=0)
        value_test = value[idx]
        label_test = label[idx]
        for repeat in range(TRAIN_TIMES):
            for sub_idx in range(len(value_train)):
                classifier.fit(value_train[sub_idx], label_train[sub_idx])
        result = classifier.classify(value_test)
        if result == label_test:
            correct_times += 1
    print('留一法验证结束,准确率:%.2f%%' % (correct_times * 100 / total_times))

运行结果

代码在 Python 3.5 环境中执行结果如下:

使用 wine.data 进行验证

10折交叉验证:当前第 1 次
准确率:84.62%
10折交叉验证:当前第 2 次
准确率:84.62%
10折交叉验证:当前第 3 次
准确率:92.31%
10折交叉验证:当前第 4 次
准确率:92.31%
10折交叉验证:当前第 5 次
准确率:69.23%
10折交叉验证:当前第 6 次
准确率:84.62%
10折交叉验证:当前第 7 次
准确率:69.23%
10折交叉验证:当前第 8 次
准确率:92.31%
10折交叉验证:当前第 9 次
准确率:84.62%
10折交叉验证:当前第 10 次
准确率:92.31%
10折交叉验证结束,平均准确率:84.62%

开始使用留一法验证
留一法验证结束,准确率:83.85%

使用 iris.data 进行验证

10折交叉验证:当前第 1 次
准确率:100.00%
10折交叉验证:当前第 2 次
准确率:100.00%
10折交叉验证:当前第 3 次
准确率:100.00%
10折交叉验证:当前第 4 次
准确率:100.00%
10折交叉验证:当前第 5 次
准确率:100.00%
10折交叉验证:当前第 6 次
准确率:100.00%
10折交叉验证:当前第 7 次
准确率:100.00%
10折交叉验证:当前第 8 次
准确率:100.00%
10折交叉验证:当前第 9 次
准确率:100.00%
10折交叉验证:当前第 10 次
准确率:100.00%
10折交叉验证结束,平均准确率:100.00%

开始使用留一法验证
留一法验证结束,准确率:100.00%

从结果可见,10折交叉验证法和留一法结果基本一致,而 Iris 数据集由于数据维度较低,预测效果明显好于 Wine 数据集。
详细代码和数据文件请访问:
https://coding.net/u/dapanbest/p/MLHomeworks/git/tree/master/LinearModel
完结撒花!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值