【火炉炼AI】机器学习018-项目案例:根据大楼进出人数预测是否举办活动
【火爐煉AI】機(jī)器學(xué)習(xí)018-項(xiàng)目案例:根據(jù)大樓進(jìn)出人數(shù)預(yù)測(cè)是否舉辦活動(dòng)
(本文所使用的Python庫和版本號(hào): Python 3.5, Numpy 1.14, scikit-learn 0.19, matplotlib 2.2 )
我們經(jīng)常看到辦公大樓中人來人往,進(jìn)進(jìn)出出,在平時(shí)沒有什么活動(dòng)的時(shí)候,進(jìn)出大樓的人數(shù)會(huì)非常少,而一旦舉辦有大型商業(yè)活動(dòng),則人山人海,熙熙攘攘,所以很明顯,大樓進(jìn)出的人數(shù)和大樓是否舉辦活動(dòng)有很明顯的關(guān)聯(lián),那么,是否可以構(gòu)建一個(gè)模型,通過大樓進(jìn)出人數(shù)來預(yù)測(cè)該大樓是否在舉辦某種活動(dòng)了?
答案是肯定的,且聽煉丹老頑童娓娓道來。
1. 準(zhǔn)備數(shù)據(jù)集
本項(xiàng)目案例所使用到的原始數(shù)據(jù)集來源于: UCI大學(xué)數(shù)據(jù)集,該數(shù)據(jù)集是公開的,讀者可以自行下載該數(shù)據(jù)集到自己的本地電腦上。
1.1 了解數(shù)據(jù)集
從該數(shù)據(jù)集的官方網(wǎng)站上,我們可以看到該數(shù)據(jù)集的基本介紹:
可以看出,該數(shù)據(jù)集一共有10080個(gè)樣本,沒有缺失數(shù)據(jù),一共有四個(gè)基本屬性,集四個(gè)features,可以用于分類模型和時(shí)序模型,此處我們只是用來進(jìn)行“是否舉辦活動(dòng)”的預(yù)測(cè),很明顯,是一個(gè)多分類問題,具體而言是一個(gè)二分類問題。
在我們下載該數(shù)據(jù)后,通過查看比對(duì),發(fā)現(xiàn)該有效數(shù)據(jù)主要存放在兩個(gè)文件中(CalIt2.data和CalIt2.events),其中CalIt2.data中存儲(chǔ)了10080條數(shù)據(jù)記錄,每一條數(shù)據(jù)包含有四列,每一列的信息說明如下表所示。而CalIt2.events包括有30條數(shù)據(jù)記錄,也包括有四列,其中的第一列是日期,第二列是活動(dòng)開始時(shí)間,第三列是活動(dòng)結(jié)束時(shí)間,第四列表示有活動(dòng)。下是我總結(jié)的本項(xiàng)目案例數(shù)據(jù)集的基本信息。
其中表格中前面四行表示數(shù)據(jù)集的四個(gè)features,這些信息位于CalIt2.data中,最后一行表示數(shù)據(jù)集的Label,位于CalIt2.events中。
1.2 數(shù)據(jù)規(guī)整
由于本項(xiàng)目的數(shù)據(jù)集位于兩個(gè)不同的文件中,同時(shí)兩個(gè)文件的樣本格式也不是一一對(duì)應(yīng),故而在構(gòu)建模型之前,需要我們對(duì)數(shù)據(jù)進(jìn)行規(guī)整,組成我們所需要的數(shù)據(jù)類型。
此處所用到的數(shù)據(jù)規(guī)整至少包括有三個(gè)方面:將同一個(gè)時(shí)間段內(nèi)進(jìn)出大樓的人數(shù)統(tǒng)一到一行,將CalIt2.events中的標(biāo)記數(shù)據(jù)加載到CalIt2.data中組成完整的數(shù)據(jù)集,將數(shù)據(jù)集中的日期轉(zhuǎn)換為星期數(shù),作為一個(gè)特征向量。
1.2.1 進(jìn)出大樓人員統(tǒng)一到一行
由于原始數(shù)據(jù)集CalIt2.data中,進(jìn)大樓的位于一行,最前面的代號(hào)為9,出大樓的位于另外一行,最前面的代號(hào)為7,故而我們需要將這兩行數(shù)據(jù)統(tǒng)一到一行,下面是實(shí)現(xiàn)代碼,所采用的核心思想是:用DataFrame的布爾索引來獲取進(jìn)出大樓的單獨(dú)一個(gè)DataFrame,然后將這兩個(gè)DF整合到一個(gè)DataFrame中。
# 將feature_set中進(jìn)樓和出樓的人員統(tǒng)計(jì)到一行。 # 目前數(shù)據(jù)集中出樓人員(代號(hào)7)位于偶數(shù)行,進(jìn)樓人員(代號(hào)9)位于奇數(shù)行。 # 可以使用for in 方式依次取出各行的人員數(shù),但此處我更愿意使用布爾型索引 code_7=feature_set[0]==7 code_7_data=feature_set[code_7].iloc[:,3].reset_index(drop=True) code_9=feature_set[0]==9 code_9_data=feature_set[code_9].reset_index(drop=True) # print(code_9_data)# OK feature_set2=code_9_data feature_set2[4]=code_7_data feature_set2.drop([0],axis=1,inplace=True) # 刪除第0列 # print(feature_set2) # col3 表示in,col4表示out,打印沒有問題 # feature_set2.to_csv('d:/feature_set2.csv') # 保存以便查看是否有誤 復(fù)制代碼打印結(jié)果可以參考我的原始代碼(我的github),此處由于打印出來后結(jié)果太長(zhǎng),我沒有貼上來。
1.2.2 將label添加到Feature_set中組成完整的數(shù)據(jù)集
由于原始數(shù)據(jù)集的features特征向量放置在CalIt2.data文件中,且Label向量放置在CalIt2.events中,故而有必要將這兩部分整合到一起。但是,整合過程并不是簡(jiǎn)單的將兩個(gè)DataFrame連接起來,而要考慮時(shí)間范圍。在CalIt2.events文件中列舉了有活動(dòng)的日期和起止時(shí)間,故而我們需要對(duì)Features中的日期和時(shí)間拿出來和CalIt2.events進(jìn)行逐一比對(duì),如果日期和時(shí)間落在events文件中,則表示這個(gè)時(shí)間段有活動(dòng),需要作出特殊標(biāo)記。有很多種方法可以是實(shí)現(xiàn)這種逐一比對(duì),下面我還是采用DataFrame的切片和索引來完成,我認(rèn)為,這種方式算是速度比較快的一種方式。
# 下面是如何將feature_set2和label_set整合到一個(gè)DataFrame中來 # 要判斷時(shí)間,如果feature_set2中的日期和時(shí)間都落在了label_set對(duì)應(yīng)的時(shí)間內(nèi), # 則表示有event發(fā)生,用1表示,如果沒有,用0表示。 # 比較日期時(shí)間的方法有很多,此處我采用比較簡(jiǎn)單的方法 feature_set2[5]=0 # 表示是否有event的列都初始化為0def calc_mins(time_str):nums=time_str.split(':')return 60*int(nums[0])+int(nums[1]) # 將時(shí)間轉(zhuǎn)換為分鐘數(shù),此處不用考慮秒for row_id,date in enumerate(label_set[0]): # 先取出label中的日期temp_df=feature_set2[feature_set2[1]==date]if temp_df is None:continuestart_min=calc_mins(label_set.iloc[row_id,1])stop_min=calc_mins(label_set.iloc[row_id,2])for row in temp_df[2]: # 在逐一判斷時(shí)間是否位于label中時(shí)間之間feature_min=calc_mins(row)if feature_min>=start_min and feature_min<=stop_min: feature_row=temp_df[temp_df[2]==row].index.tolist()feature_set2.loc[feature_row,5]=1 # feature_set2.to_csv('d:/feature_set2_withLabel.csv') # 保存后打印查看沒有問題 復(fù)制代碼1.2.3 將日期轉(zhuǎn)換為星期數(shù)
這個(gè)相對(duì)于前面兩個(gè)數(shù)據(jù)規(guī)整方面,要簡(jiǎn)單得多,直接貼代碼。
# 進(jìn)一步處理,由于日期在以后的日子里不可重復(fù),作為feature并不合適,而可以用星期數(shù)來代替, feature_set2[0]=pd.to_datetime(feature_set2[1]) # print(feature_set2.tail()) feature_set2[0]=feature_set2[0].map(lambda x: x.strftime('%w')) # 將日期轉(zhuǎn)換為星期數(shù) feature_set2=feature_set2.reindex(columns=range(6)) print(feature_set2.tail()) # 查看轉(zhuǎn)換沒有問題 feature_set2.to_csv('E:\PyProjects\DataSet\BuildingInOut/Sorted_Set.txt') # 將整理好的數(shù)據(jù)集保存,下次可以直接讀取 復(fù)制代碼-----------------------輸---------出--------------------------------
0 1 2 3 4 5 5035 6 11/05/05 21:30:00 0 0 0 5036 6 11/05/05 22:00:00 0 3 0 5037 6 11/05/05 22:30:00 0 0 0 5038 6 11/05/05 23:00:00 0 0 0 5039 6 11/05/05 23:30:00 0 1 0
-------------------------完-------------------------------------
處理完成之后的feature_set2可以直接保存一下,這樣下次可以直接調(diào)用這個(gè)數(shù)據(jù)規(guī)整之后的文件,讀取里面的數(shù)據(jù)集來訓(xùn)練和測(cè)試即可。
1.3 數(shù)據(jù)編碼
很明顯,從上面的數(shù)據(jù)集中可以看到第1列是日期,不適合做特征向量,需要?jiǎng)h除,而第2列是字符串類型,沒法用于機(jī)器學(xué)習(xí),故而我們需要對(duì)第2列進(jìn)行編碼,如下代碼:
# 由于第1列只是包含日期,作為特征向量并不合適,故而需要?jiǎng)h除 feature_set2.drop([1],axis=1,inplace=True) # 而第2列明顯是字符串類型,里面的內(nèi)容對(duì)機(jī)器學(xué)習(xí)而言如同天書,故需要編碼 from sklearn import preprocessing time_encoder=preprocessing.LabelEncoder() feature_set2[2]=time_encoder.fit_transform(feature_set2[2]) print(feature_set2.tail()) 復(fù)制代碼------------------輸---------出--------------------------------
0 2 3 4 5 5035 6 43 0 0 0 5036 6 44 0 3 0 5037 6 45 0 0 0 5038 6 46 0 0 0 5039 6 47 0 1 0
---------------------完-------------------------------------
所以,可以看到,第2列已經(jīng)變成了數(shù)字編碼,這部分內(nèi)容在我以前的文章中用到過很多次。如【火爐煉AI】機(jī)器學(xué)習(xí)013-用樸素貝葉斯分類器估算個(gè)人收入階層
########################小**********結(jié)#################
1. 在網(wǎng)絡(luò)上,我找了好久都沒有找到如何處理這個(gè)數(shù)據(jù)集,好多都是直接給出處理后的結(jié)果,所以此處我就自己對(duì)這個(gè)數(shù)據(jù)集進(jìn)行了規(guī)整,并將規(guī)整的代碼放出來。
2. 數(shù)據(jù)集的規(guī)整和預(yù)處理往往會(huì)花掉機(jī)器學(xué)習(xí)的大部分時(shí)間,此處因?yàn)橐呀?jīng)有了現(xiàn)成數(shù)據(jù),僅僅是對(duì)數(shù)據(jù)進(jìn)行規(guī)整,故而耗時(shí)相對(duì)較少。
3. Pandas和Numpy基本上是數(shù)據(jù)預(yù)處理,數(shù)據(jù)清洗,數(shù)據(jù)規(guī)整的有力神器,一定要熟練掌握。
#####################################################
2. 使用SVM構(gòu)建分類器
SVM分類器的構(gòu)建和其他項(xiàng)目案例中并沒有太大差別,可以直接參考我的其他文章:【火爐煉AI】機(jī)器學(xué)習(xí)014-用SVM構(gòu)建非線性分類模型.下面直接貼出代碼。
# 下面是使用SVM構(gòu)建分類器 from sklearn.svm import SVC classifier=SVC(kernel='rbf',probability=True,class_weight='balanced') classifier.fit(train_X,train_y) 復(fù)制代碼-------------------輸---------出--------------------------------
SVC(C=1.0, cache_size=200, class_weight='balanced', coef0=0.0, decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf', max_iter=-1, probability=True, random_state=None, shrinking=True, tol=0.001, verbose=False)
-------------------------完-------------------------------------
# 用交叉驗(yàn)證來檢驗(yàn)?zāi)P偷臏?zhǔn)確性,只是在test set上驗(yàn)證準(zhǔn)確性 from sklearn.cross_validation import cross_val_score num_validations=5 accuracy=cross_val_score(classifier,test_X,test_y,scoring='accuracy',cv=num_validations) print('準(zhǔn)確率:{:.2f}%'.format(accuracy.mean()*100)) precision=cross_val_score(classifier,test_X,test_y,scoring='precision_weighted',cv=num_validations) print('精確度:{:.2f}%'.format(precision.mean()*100)) recall=cross_val_score(classifier,test_X,test_y,scoring='recall_weighted',cv=num_validations) print('召回率:{:.2f}%'.format(recall.mean()*100)) f1=cross_val_score(classifier,test_X,test_y,scoring='f1_weighted',cv=num_validations) print('F1 值:{:.2f}%'.format(f1.mean()*100)) 復(fù)制代碼-----------------------輸---------出--------------------------------
準(zhǔn)確率:93.78%
精確度:92.96%
召回率:93.78%
F1 值:92.96%
----------------------------完-------------------------------------
從交叉驗(yàn)證的結(jié)果可以看出,在測(cè)試集上,該SVM分類器仍然可以得到各項(xiàng)指標(biāo)都達(dá)到90%以上的效果,可以認(rèn)為,該SVM分類器的效果比較好。當(dāng)然,如果還想繼續(xù)提升該分類效果,可以使用GridSearch方法來搜索最佳參數(shù)組合,GridSearch的使用方法可以參考我的文章【火爐煉AI】機(jī)器學(xué)習(xí)017-使用GridSearch搜索最佳參數(shù)組合
3. 使用該分類器對(duì)新樣本進(jìn)行預(yù)測(cè)
首先我們需要新樣本數(shù)據(jù),此處我自己構(gòu)建了一些新樣本,并不一定準(zhǔn)確,但是可以用于預(yù)測(cè)。對(duì)新樣本的預(yù)測(cè)也很簡(jiǎn)單,直接使用classifier.predict()函數(shù)即可。
# 看起來該模型的預(yù)測(cè)效果很不錯(cuò) # 那么用它來對(duì)新樣本數(shù)據(jù)進(jìn)行預(yù)測(cè),會(huì)是什么樣了? new_samples=np.array([[2,'09:30:00',20,12], # 即某個(gè)星期二,上午9點(diǎn)-9點(diǎn)半時(shí)間段,進(jìn)大樓20人,出大樓12人[2,'11:30:00',26,9],[6,'12:30:00',4,22],[0,'05:00:00',1,0]]) transformed=time_encoder.transform(new_samples[:,1]) # print(transformed) # 檢查OK new_samples[:,1]=transformed print(new_samples)# 使用classifier進(jìn)行預(yù)測(cè) output_class = classifier.predict(new_samples) print('has events? {}'.format(output_class)) 復(fù)制代碼---------------------輸---------出--------------------------------
[['2' '19' '20' '12']
['2' '23' '26' '9']
['6' '25' '4' '22']
['0' '10' '1' '0']]
has events? [0 1 0 0]
----------------------------完-------------------------------------
從輸出結(jié)果可以看出,只有第二個(gè)樣本有活動(dòng),其他樣本都沒有活動(dòng),可以想象的出來,第一個(gè)樣本9點(diǎn)-9點(diǎn)半有很多人進(jìn)樓,但是少量人出樓,會(huì)不會(huì)是正常的上班族?而第二個(gè)樣本11點(diǎn)到11點(diǎn)半之間有很多人進(jìn)來,卻很少有了出去,這個(gè)時(shí)間段應(yīng)該就是會(huì)議期間正常的人員流動(dòng),而第三個(gè)樣本12點(diǎn)到12點(diǎn)半,有很多人出樓,這大概是人家出去吃午飯吧。而第四個(gè)樣本,周日的凌晨五點(diǎn),估計(jì)只有鬼才能在大樓門口晃悠了。。。。
########################小**********結(jié)#################
1. 這個(gè)項(xiàng)目的難點(diǎn)不在于SVM模型的構(gòu)建和分類訓(xùn)練上,而在于前期數(shù)據(jù)集的規(guī)整上。
2. 后面使用SVM進(jìn)行模型構(gòu)建和訓(xùn)練,對(duì)新樣本進(jìn)行預(yù)測(cè),在我前面的文章中講了很多次了,至此已經(jīng)沒有什么新意了。
#####################################################
注:本部分代碼已經(jīng)全部上傳到(我的github)上,歡迎下載。
參考資料:
1, Python機(jī)器學(xué)習(xí)經(jīng)典實(shí)例,Prateek Joshi著,陶俊杰,陳小莉譯
總結(jié)
以上是生活随笔為你收集整理的【火炉炼AI】机器学习018-项目案例:根据大楼进出人数预测是否举办活动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: window服务器上搭建git服务,wi
- 下一篇: 操作系统和常用软件下载