李宏毅 机器学习 作业 2 Hungyi.Li Machine Learning HW2 逻辑回归法实现以及优化

本文主要是介绍李宏毅 机器学习 作业 2 Hungyi.Li Machine Learning HW2 逻辑回归法实现以及优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

关于“一个属性有几个固定选项,怎么表达”的问题,数据采用one-hot,每个选项独立成一个属性,loss用交叉熵计算,这样更科学。这里不用过多操心,数据给出已经处理好了。

 

有一段可能是助教给的参考代码,不过参考代码有些漏洞(在代码下方贴出),需要修改下,然后还有一定的调参提升空间。

 


参考代码如下:

 

import os, sys
import numpy as np
from random import shuffle
import argparse
from math import log, floor
import pandas as pd# If you wish to get the same shuffle result
# np.random.seed(2401)def load_data(train_data_path, train_label_path, test_data_path):X_train = pd.read_csv(train_data_path, sep=',', header=0)X_train = np.array(X_train.values)Y_train = pd.read_csv(train_label_path, sep=',', header=0)Y_train = np.array(Y_train.values)X_test = pd.read_csv(test_data_path, sep=',', header=0)X_test = np.array(X_test.values)return (X_train, Y_train, X_test)def _shuffle(X, Y):randomize = np.arange(len(X))np.random.shuffle(randomize)return (X[randomize], Y[randomize])def normalize(X_all, X_test):# Feature normalization with train and test XX_train_test = np.concatenate((X_all, X_test))mu = (sum(X_train_test) / X_train_test.shape[0])sigma = np.std(X_train_test, axis=0)mu = np.tile(mu, (X_train_test.shape[0], 1))sigma = np.tile(sigma, (X_train_test.shape[0], 1))X_train_test_normed = (X_train_test - mu) / sigma# Split to train, test againX_all = X_train_test_normed[0:X_all.shape[0]]X_test = X_train_test_normed[X_all.shape[0]:]return X_all, X_testdef split_valid_set(X_all, Y_all, percentage):all_data_size = len(X_all)valid_data_size = int(floor(all_data_size * percentage))#这个比例反了。X_all, Y_all = _shuffle(X_all, Y_all)X_train, Y_train = X_all[0:valid_data_size], Y_all[0:valid_data_size]X_valid, Y_valid = X_all[valid_data_size:], Y_all[valid_data_size:]return X_train, Y_train, X_valid, Y_validdef sigmoid(z):res = 1 / (1.0 + np.exp(-z))return np.clip(res, 1e-8, 1-(1e-8))def valid(w, b, X_valid, Y_valid):valid_data_size = len(X_valid)z = (np.dot(X_valid, np.transpose(w)) + b)y = sigmoid(z)y_ = np.around(y)result = (np.squeeze(Y_valid) == y_)print('Validation acc = %f' % (float(result.sum()) / valid_data_size))returndef train(X_all, Y_all, save_dir):# Split a 10%-validation set from the training setvalid_set_percentage = 0.1X_train, Y_train, X_valid, Y_valid = split_valid_set(X_all, Y_all, valid_set_percentage)# Initiallize parameter, hyperparameterw = np.zeros((106,))b = np.zeros((1,))l_rate = 0.1batch_size = 32train_data_size = len(X_train)step_num = int(floor(train_data_size / batch_size))epoch_num = 1000save_param_iter = 50# Start trainingtotal_loss = 0.0for epoch in range(1, epoch_num):# Do validation and parameter savingif (epoch) % save_param_iter == 0:print('=====Saving Param at epoch %d=====' % epoch)if not os.path.exists(save_dir):os.mkdir(save_dir)np.savetxt(os.path.join(save_dir, 'w'), w)np.savetxt(os.path.join(save_dir, 'b'), [b,])print('epoch avg loss = %f' % (total_loss / (float(save_param_iter) * train_data_size)))total_loss = 0.0valid(w, b, X_valid, Y_valid)# Random shuffleX_train, Y_train = _shuffle(X_train, Y_train)# Train with batchfor idx in range(step_num):X = X_train[idx*batch_size:(idx+1)*batch_size]Y = Y_train[idx*batch_size:(idx+1)*batch_size]z = np.dot(X, np.transpose(w)) + by = sigmoid(z)cross_entropy = -1 * (np.dot(np.squeeze(Y), np.log(y)) + np.dot((1 - np.squeeze(Y)), np.log(1 - y)))total_loss += cross_entropyw_grad = np.sum(-1 * X * (np.squeeze(Y) - y).reshape((batch_size,1)), axis=0)b_grad = np.sum(-1 * (np.squeeze(Y) - y))# SGD updating parametersw = w - l_rate * w_gradb = b - l_rate * b_gradreturndef infer(X_test, save_dir, output_dir):test_data_size = len(X_test)# Load parametersprint('=====Loading Param from %s=====' % save_dir)w = np.loadtxt(os.path.join(save_dir, 'w'))b = np.loadtxt(os.path.join(save_dir, 'b'))# predictz = (np.dot(X_test, np.transpose(w)) + b)y = sigmoid(z)y_ = np.around(y)print('=====Write output to %s =====' % output_dir)if not os.path.exists(output_dir):os.mkdir(output_dir)output_path = os.path.join(output_dir, 'log_prediction.csv')with open(output_path, 'w') as f:f.write('id,label\n')for i, v in  enumerate(y_):f.write('%d,%d\n' %(i+1, v))returndef main(opts):# Load feature and labelX_all, Y_all, X_test = load_data(opts.train_data_path, opts.train_label_path, opts.test_data_path)# NormalizationX_all, X_test = normalize(X_all, X_test)# To train or to inferif opts.train:train(X_all, Y_all, opts.save_dir)elif opts.infer:infer(X_test, opts.save_dir, opts.output_dir)else:print("Error: Argument --train or --infer not found")returnif __name__ == '__main__':parser = argparse.ArgumentParser(description='Logistic Regression with Gradient Descent Method')group = parser.add_mutually_exclusive_group()group.add_argument('--train', action='store_true', default=False,dest='train', help='Input --train to Train')group.add_argument('--infer', action='store_true',default=False,dest='infer', help='Input --infer to Infer')parser.add_argument('--train_data_path', type=str,default='feature/X_train', dest='train_data_path',help='Path to training data')parser.add_argument('--train_label_path', type=str,default='feature/Y_train', dest='train_label_path',help='Path to training data\'s label')parser.add_argument('--test_data_path', type=str,default='feature/X_test', dest='test_data_path',help='Path to testing data')parser.add_argument('--save_dir', type=str,default='logistic_params/', dest='save_dir',help='Path to save the model parameters')parser.add_argument('--output_dir', type=str,default='logistic_output/', dest='output_dir',help='Path to save the model parameters')opts = parser.parse_args()main(opts)

代码错误有几点:

1.每一个epoch中,用于计算cross_entropy从而统计进total_loss的数据量,是step_num*batch_size,小于train_data_size。而ta

它用的是train_data_size。

这两个数据对比:结果证实,确实有点偏差,不过因为他只是打印,不是训练过程,所以看起来也没影响结果。

2.split_valid_set中,训练集和验证集划分错了,用验证集的比例,提取了训练集。

这个也很明显,会对结果产生显著影响

3.感谢网友weixin_42183288提醒,不过此处确实没错:交叉熵隐含了sum操作!

因为np.dot是点积,点积已经等于sum了,batch_size个样本,最终只输出一维结果,对于batch_size个样本,loss既然是求和,grad也应该是求和,grad要和loss保持同步,所以grad就是sum。

print('epoch avg loss = %f' % (total_loss / (float(save_param_iter) * train_data_size)))

从这句打印也看得出,loss确实是所有样本的和,然后才除以迭代epoch次数和data_size,平均到每个样本。

但是打印归打印,这句不是也说了是avg loss嘛!实际的total loss,还是针对batch的sum。

 

其实这根本不是应该纠结的点!!!这可能是接触代码不多的新手都会碰到的疑惑:

“课程中是除以m,也就是mean”

“loss要用sum还是mean?”

“grad要用sum还是mean?”

“为什么我用mean比用sum感觉要更准一点?”

本质上,这是没区别的,梯度下降,下降的是什么?loss的值!具体loss或者w的大小,只与计算机对小数的表达能力有关(这个角度看,mean反而不如sum准),而不太可能上升到模型准确度差异的高度!教科书是为了示意,如果他想精准地表达梯度,当然要除以m,可是实际计算当中,区别不大。

无论你的grad是用了sum还是mean,最终都是一个数值(乘以参数数量),是数值,在梯度下降时就逃不过乘以l_rate这一环,如果你觉得你用了mean比sum训练结果更准,可以把sum版本对应的l_rate调小一点(实际训练中往往是变动的学习率,越到后期越小)

            w_grad = np.mean(-1 * X * (np.squeeze(Y) - y).reshape((batch_size,1)), axis=0)b_grad = np.mean(-1 * (np.squeeze(Y) - y))# SGD updating parametersw = w - l_rate * w_gradb = b - l_rate * b_grad

 

 

 

4.还有个什么错误我忘了。。。。

 

 

上传kaggle,这是两条分数分段线:

0.84952高分

0.84215及格

 

验证集分配错误的,也够及格

0.84803

 

优化的分配比例的:

        X_valid,Y_valid = X_all[0:valid_data_size],Y_all[0:valid_data_size]X_train,Y_train = X_all[valid_data_size:],Y_all[valid_data_size:]

0.85159

下边是调整其他参数,batch_size和epoch等:

batch_size提升到64,epoch到5000

0.85356

batch_size提升到64,epoch到5000,l_rate到0.5

0.84201

会下降,猜测可能是没用adagrad,这里不收敛了。

batch_size提升到64,epoch到5000,l_rate改到0.2,

0.85036

比0.5要强,不过还是比0.1差些,也许有运气成分,每次训练也不一样。

batch_size提升到64,epoch到5000,l_rate改到0.05,

 

0.85380目前最优,可能是epoch数量比较大,抵消了learning rate的降低效应。可以明显看到,learning调大明显有收敛问题,所以adagrad可能效果更好。

 

还有训练,集和验证集的比例可以调:

batch_size提升到64,epoch到5000,l_rate改到0.05,验证集改到0.3(默认0.1)

validation acc = 0.851761

实际分数0.85417目前最优,可能说明之前的有过拟合现象。

其他不动,验证集比例改回0.1重新测

validation acc = 0.860258
分数:0.85417,居然一样

本地validation  acc更高一些,实际分数还一样,是有一点过拟合吧??!!

调了一下记录,可以看到倒数第二次,虽然公开的也是0.85417,和最后一次一样,但是最后一次在private衰减到了0.84891,证明最后一次确实比倒数第二次过拟合

但是过拟合和过拟合也不一样,有两种过拟合,这里说明最后一次是针对线上公开成绩的过拟合,private set发生衰减。

前边说的是针对验证集的过拟合,线上答案发生衰减。

 

紫色字体不算绝对严谨,因为需要更细致的测试对比,每次训练本来就有微小的差距。这里主要讨论可能存在的问题和优化思路,因为时间关系和运算量的关系,就不在这纠结了,先结了。

 

实际跑了一下adagrad,效果飘忽,调起来比较麻烦,时间问题,略过

                w_s_gra = np.zeros(106)b_s_gra = np.zeros(1)for idx in range(step_num):X = X_train[idx*batch_size:(idx+1)*batch_size]Y = Y_train[idx*batch_size:(idx+1)*batch_size]z = np.dot(X,np.transpose(w))+by = sigmoid(z)cross_entropy = -1*(np.dot(np.squeeze(Y),np.log(y))+np.dot(1-np.squeeze(Y),np.log(1-y)))total_loss += cross_entropyw_grad = np.mean(-1*X*(np.squeeze(Y)-y).reshape((batch_size,1)),axis=0)b_grad = np.mean(-1*(np.squeeze(Y)-y))w_s_gra += w_grad**2b_s_gra += b_grad**2w_ada = np.sqrt(w_s_gra)b_ada = np.sqrt(b_s_gra)w = w - l_rate * w_grad/w_adab = b - l_rate * b_grad/b_ada

 

 

可能有编辑错的地方,凑合看吧,实在懒得多写了,这个csdn现在真难用,拼音打一半就自动换行了。

 

 

 

这篇关于李宏毅 机器学习 作业 2 Hungyi.Li Machine Learning HW2 逻辑回归法实现以及优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/548617

相关文章

SpringBoot利用树形结构优化查询速度

《SpringBoot利用树形结构优化查询速度》这篇文章主要为大家详细介绍了SpringBoot利用树形结构优化查询速度,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一个真实的性能灾难传统方案为什么这么慢N+1查询灾难性能测试数据对比核心解决方案:一次查询 + O(n)算法解决

Go语言使用sync.Mutex实现资源加锁

《Go语言使用sync.Mutex实现资源加锁》数据共享是一把双刃剑,Go语言为我们提供了sync.Mutex,一种最基础也是最常用的加锁方式,用于保证在任意时刻只有一个goroutine能访问共享... 目录一、什么是 Mutex二、为什么需要加锁三、实战案例:并发安全的计数器1. 未加锁示例(存在竞态)

基于Redisson实现分布式系统下的接口限流

《基于Redisson实现分布式系统下的接口限流》在高并发场景下,接口限流是保障系统稳定性的重要手段,本文将介绍利用Redisson结合Redis实现分布式环境下的接口限流,具有一定的参考价值,感兴趣... 目录分布式限流的核心挑战基于 Redisson 的分布式限流设计思路实现步骤引入依赖定义限流注解实现

SpringBoot实现虚拟线程的方案

《SpringBoot实现虚拟线程的方案》Java19引入虚拟线程,本文就来介绍一下SpringBoot实现虚拟线程的方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录什么是虚拟线程虚拟线程和普通线程的区别SpringBoot使用虚拟线程配置@Async性能对比H

基于Python实现进阶版PDF合并/拆分工具

《基于Python实现进阶版PDF合并/拆分工具》在数字化时代,PDF文件已成为日常工作和学习中不可或缺的一部分,本文将详细介绍一款简单易用的PDF工具,帮助用户轻松完成PDF文件的合并与拆分操作... 目录工具概述环境准备界面说明合并PDF文件拆分PDF文件高级技巧常见问题完整源代码总结在数字化时代,PD

Python实现Word转PDF全攻略(从入门到实战)

《Python实现Word转PDF全攻略(从入门到实战)》在数字化办公场景中,Word文档的跨平台兼容性始终是个难题,而PDF格式凭借所见即所得的特性,已成为文档分发和归档的标准格式,下面小编就来和大... 目录一、为什么需要python处理Word转PDF?二、主流转换方案对比三、五套实战方案详解方案1:

SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南

《SpringBoot集成EasyExcel实现百万级别的数据导入导出实践指南》本文将基于开源项目springboot-easyexcel-batch进行解析与扩展,手把手教大家如何在SpringBo... 目录项目结构概览核心依赖百万级导出实战场景核心代码效果百万级导入实战场景监听器和Service(核心

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析

基于Python Playwright进行前端性能测试的脚本实现

《基于PythonPlaywright进行前端性能测试的脚本实现》在当今Web应用开发中,性能优化是提升用户体验的关键因素之一,本文将介绍如何使用Playwright构建一个自动化性能测试工具,希望... 目录引言工具概述整体架构核心实现解析1. 浏览器初始化2. 性能数据收集3. 资源分析4. 关键性能指

使用Redis快速实现共享Session登录的详细步骤

《使用Redis快速实现共享Session登录的详细步骤》在Web开发中,Session通常用于存储用户的会话信息,允许用户在多个页面之间保持登录状态,Redis是一个开源的高性能键值数据库,广泛用于... 目录前言实现原理:步骤:使用Redis实现共享Session登录1. 引入Redis依赖2. 配置R