python 循环十次_python机器学习——十次交叉验证训练的数据准备算法
攝于 2017年4月21日 臺(tái)灣墾丁船帆石海灘
前言
這兩天本來打算開始寫樸素貝葉斯分類器的算法的,由于上一篇博文python實(shí)現(xiàn)貝葉斯推斷——垃圾郵件分類在實(shí)現(xiàn)時(shí),在數(shù)據(jù)劃分訓(xùn)練集和測試集的時(shí)候遇到兩個(gè)問題,第一是數(shù)據(jù)量太少,只有50條數(shù)據(jù),解決方法就是擴(kuò)大數(shù)據(jù)量咯。第二個(gè),也是今天寫這篇博文的目的,就是在訓(xùn)練的時(shí)候,我先把數(shù)據(jù)文件進(jìn)行隨機(jī)亂序,然后抽取了亂序后前10個(gè)數(shù)據(jù)文件,這個(gè)目的實(shí)際上就是為了隨機(jī)從中抽取10個(gè)數(shù)據(jù)文件作為測試集,剩下的40個(gè)數(shù)據(jù)文件作為訓(xùn)練集。這種方法在機(jī)器學(xué)習(xí)的數(shù)據(jù)準(zhǔn)備過程中是非常常見的。但是,為了能夠更好的測試模型,盡可能的排除外在因素的干擾,消除偏好,同時(shí)獲得最好的精度,所以這里則引入交叉驗(yàn)證(Cross-Validation),而交叉驗(yàn)證的次數(shù)一般取10次,所以一般也叫十次交叉驗(yàn)證。從stackoverflow上找到一張圖,一看即明,原圖網(wǎng)站。
算法思路
如果數(shù)據(jù)量為10的倍數(shù),則分離數(shù)據(jù)是非常方便的,即直接均分十份,依次輪流存入test和train兩個(gè)文件夾中。
但是如果不是10的倍數(shù)呢?
思路如下:
1.獲取數(shù)據(jù)數(shù)量,余除10,獲得余數(shù),例如現(xiàn)在有24個(gè)數(shù)據(jù)文件,24%10=4
2.根據(jù)余數(shù)拆分?jǐn)?shù)據(jù),即將數(shù)據(jù)拆分成20和4兩份。
3.將拆分?jǐn)?shù)據(jù)后能夠整除10的一份均分十份,即將數(shù)據(jù)量為20的數(shù)據(jù)量均分為十份,每份包含2個(gè)數(shù)據(jù)文件。
4.將余數(shù)拆分成一份一個(gè)數(shù)據(jù),然后遍歷每一份,依次一份分開加入到第3步已經(jīng)均分好的數(shù)據(jù)中,直到余數(shù)部分的數(shù)據(jù)使用完。
沒看懂?
甩圖:
2.png
這樣做的目的是盡可能達(dá)到數(shù)據(jù)均分的效果,讓實(shí)驗(yàn)效果更加。
算法實(shí)現(xiàn)
前期準(zhǔn)備
數(shù)據(jù)來源
700條neg電影數(shù)據(jù)+700條pos電影數(shù)據(jù),共1400條數(shù)據(jù)。下一篇博客樸素貝葉斯分類器將會(huì)用到這批數(shù)據(jù)。完整數(shù)據(jù)可以在下方的github獲得。
python包
1.fwalker
2.bfile
3.numpy
4.shutil
5.os
fwalker和bfile是不是很陌生?哈哈這是我自己寫的包,傳送門:python3文本讀取與寫入常用代碼、python中import自己寫的.py
當(dāng)一些代碼(如寫入寫出文本,創(chuàng)建文件夾、統(tǒng)計(jì)詞頻)經(jīng)常需要被使用到時(shí),可以考慮下我這種方法,非常方便,可以大大縮短編寫算法的周期和減少代碼量。
代碼實(shí)現(xiàn)
既然我們需要進(jìn)行十次交叉驗(yàn)證,因此數(shù)據(jù)需要復(fù)制十份,則需要10個(gè)文件夾來進(jìn)行存放,每個(gè)文件夾下又包含test和train數(shù)據(jù)。
代碼(寫一行代碼打一行注釋,良心啊~)
# -*- coding: utf-8 -*-
# @Date : 2017-05-11 21:24:50
# @Author : Alan Lau (rlalan@outlook.com)
# @Version : Python3.5
from fwalker import fun
from bfile import buildfile as bf
from random import shuffle
import numpy as np
import shutil
import os
def buildfile(output_path):
for i in range(1, 10+1):
# 循環(huán)新建編號(hào)1-10的文件夾,用于存放train和test文件夾
file_num = bf('%s\\%d' % (output_path, i))
# 在每個(gè)編號(hào)文件夾下新建train文件夾,用于存放90%的訓(xùn)練數(shù)據(jù)
train_file = bf('%s\\train' % file_num)
# 在每個(gè)編號(hào)文件夾下新建test文件夾,用于存放10%的訓(xùn)練數(shù)據(jù)
test_file = bf('%s\\test' % file_num)
print('Data storage has been bulit!')
return output_path
def split_ten(files):
file_len = len(files) # 獲取文件總數(shù)
shuffle(files) # 隨機(jī)打亂文件路徑列表的順序,即使python的隨機(jī)是偽隨機(jī)
data_storage = [] # 初始化一個(gè)列表,用來接收分劃分好的文件路徑
remainder = file_len % 10 # 判斷文件數(shù)量能否直接被10整除
if remainder == 0: # 如果可以整除,直接將數(shù)據(jù)切成10組
np_files = np.array(files) # 將文件路徑列表轉(zhuǎn)換成numpy
data_storage = np_files.reshape(10, -1) # 利用numpy的reshape來將文件路徑切分為10組
# 比如說現(xiàn)在有20個(gè)文件路徑
# reshape()后得到的結(jié)果為2、2、2、2、2、2、2、2、2、2,即共十份、每份包含2個(gè)文件路徑。
return data_storage
else: # 否則,則先切開余數(shù)部分的文件
np_files = np.array(files[:-1*remainder]) # 切開余數(shù)部分的文件,使文件數(shù)量保證能夠被10整除
data_storage_ten = np_files.reshape(10, -1) # 同樣利用上面的方法使用numpy切分10組文件
# 獲取余數(shù)部分的文件列表,遍歷列表,盡可能的將多余的文件分散在10組文件中,而不是直接加入到一個(gè)文件中
remainder_files = (
np.array(files[-1*remainder:])).reshape(remainder, -1) # 使用reshape切分問一份一組
for i in range(0, len(remainder_files)):
ech_dst = data_storage_ten[i]
ech_rf = remainder_files[i]
# 將取出來的余數(shù)內(nèi)的路徑分別加入到已經(jīng)均分好的10份的前remainder個(gè)數(shù)據(jù)當(dāng)中,比如說現(xiàn)在有24份文件,
# 將24拆份拆分成一個(gè)能被10整除的數(shù)和一個(gè)余數(shù),即這里拆分成20和4,我們首先將拆出來的20份文件均分10份,
# 即每份有2個(gè)文件路徑,然后,再將剩下后面的4個(gè)文件路徑,盡可能的放入到剛剛均分好的10份數(shù)據(jù)中。
# 因此最終拆分的結(jié)果共有十份,每份數(shù)量分別為:3、3、3、3、2、2、2、2、2、2。
data_storage.append(np.concatenate((ech_dst, ech_rf)))
for j in range(remainder, len(data_storage_ten)):
# 將將剩下的沒有被余數(shù)部分加入的份加入到data_storage中
data_storage.append(data_storage_ten[j])
return np.array(data_storage)
def group_data(data_storage, output_path):
for i in range(0, len(data_storage)):
ech_path = '%s\\%d' % (output_path, i+1) # 構(gòu)造每一份需要寫入的路徑
ech_train_path = '%s\\train' % ech_path
ech_test_path = '%s\\test' % ech_path
test_paths = data_storage[i]
move_file(test_paths, ech_test_path)
train_paths = np.concatenate(([data_storage[:i], data_storage[i+1:]]))
# 將剩下的訓(xùn)練部分加入到train_paths中,并且降維
train_paths = np.concatenate((train_paths)) # 再次降維,使其變成1維
move_file(train_paths, ech_train_path)
num = i+1
print('No.%d is over!' % num)
def move_file(old_paths, new_path):
for old_path in old_paths:
shutil.copy2(old_path, new_path)
flag_name = '_'.join(old_path.split('\\')[-2:])
old_name = '%s\\%s' % (new_path, old_path.split('\\')[-1])
new_name = '%s\\%s' % (new_path, flag_name)
os.rename(old_name, new_name)
def main():
file_path = r'..\data\data_of_movie'
# file_path = r'..\data\test'
output_path = r'..\data\tenTimesTraining'
files = fun(file_path)
output_path = buildfile(output_path)
data_storage = split_ten(files)
group_data(data_storage, output_path)
if __name__ == '__main__':
main()
至于實(shí)驗(yàn)的結(jié)果,就是把1400條數(shù)據(jù)隨打亂,輪流存入10個(gè)文件夾中,并且其中的10%輪流作為測試集存入test文件中,剩下的90%則存入了train。
由于數(shù)據(jù)量大,我這里截取一小部分?jǐn)?shù)據(jù)展示。
對(duì)比沒有劃分?jǐn)?shù)據(jù)前的數(shù)據(jù)名稱會(huì)發(fā)現(xiàn),我這里還把類別的標(biāo)簽加到了每條數(shù)據(jù)的名稱中。
這個(gè)算法可以使用到大多數(shù)的機(jī)器學(xué)習(xí)需要?jiǎng)澐值臄?shù)據(jù)當(dāng)中,前提只需要將不同分類的數(shù)據(jù)存在以分類命名的文件夾下即可。為了方便識(shí)別,算法會(huì)提取文件夾的名稱作為標(biāo)簽名重命名每條數(shù)據(jù)。重命名格式為:標(biāo)簽_xxx.txt
所有數(shù)據(jù)以及完整代碼GITHUB
總結(jié)
以上是生活随笔為你收集整理的python 循环十次_python机器学习——十次交叉验证训练的数据准备算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html文档在word打开是乱码怎么解决
- 下一篇: halcon小例:ORC识别