機(jī)器學(xué)習(xí)(06)——K近鄰算法實(shí)戰(zhàn)(k近鄰算法實(shí)驗(yàn))

      網(wǎng)友投稿 985 2025-03-31

      學(xué)習(xí)機(jī)器學(xué)習(xí)算法,最難的不是算法及公式推導(dǎo)的學(xué)習(xí),因?yàn)檫@些很多都是成熟的現(xiàn)成的,有代碼例子可以直接使用。最難的是將算法應(yīng)用到實(shí)際的項(xiàng)目當(dāng)中。


      1. 算法概念

      K最近鄰(k-Nearest Neighbor,KNN)分類算法,是一個(gè)理論上比較成熟的方法,也是最簡(jiǎn)單的機(jī)器學(xué)習(xí)算法之一。該方法的思路是:在特征空間中,如果一個(gè)樣本附近的k個(gè)最近(即特征空間中最鄰近)樣本的大多數(shù)屬于某一個(gè)類別,則該樣本也屬于這個(gè)類別。

      使用KNN算法進(jìn)行分析預(yù)測(cè),K值的選擇、距離度量和分類決策規(guī)則是該算法的三個(gè)基本要素,直接影響預(yù)測(cè)的準(zhǔn)確率。

      在運(yùn)用KNN算法時(shí),我們通常需要將相關(guān)的特征值轉(zhuǎn)換為數(shù)值型,而該數(shù)值的大小、與其他特征的關(guān)聯(lián)度或數(shù)值間隔關(guān)系的設(shè)計(jì)(也就是分類決策規(guī)則),都會(huì)直接影響各數(shù)據(jù)之間的距離,而最終預(yù)測(cè)計(jì)算時(shí),所選擇樣本的數(shù)量(K值大小),都會(huì)直接影響最終結(jié)果的準(zhǔn)確率。

      比如下圖中的例子,當(dāng)K=3時(shí),答案為紅色三角形,而K=5時(shí),結(jié)果卻變?yōu)樗{(lán)色正方形。

      2. KNN實(shí)現(xiàn)公司電腦開關(guān)機(jī)預(yù)測(cè)

      1)項(xiàng)目說(shuō)明

      我們的電腦里安裝了各種軟件,這些軟件會(huì)時(shí)不時(shí)訪問(wèn)外網(wǎng),通過(guò)防火墻日志記錄,我們可以從中分析出公司電腦的開關(guān)機(jī)情況,查看哪些電腦正常開機(jī)關(guān)機(jī),哪些沒(méi)有一直沒(méi)有關(guān)機(jī),哪些沒(méi)有開機(jī)。

      使用KNN算法,實(shí)現(xiàn)對(duì)公司電腦開關(guān)機(jī)狀態(tài)的預(yù)測(cè)功能。

      2)日志信息

      通過(guò)查看防火墻日志,可以看到日志中有日期、防火墻設(shè)備名稱、源ip地址(局域網(wǎng)ip)、網(wǎng)卡mac地址等信息

      date=2020-01-10?time=00:13:08?devname="AW-B1-901"?devid="FG100ETK18038642"?logid="0000000020"?type="traffic"?subtype="forward"?level="notice"?vd="root"?eventtime=1578586388984224524?tz="+0800"?srcip=192.168.10.13?srcport=16701?srcintf="Local-Office"?srcintfrole="lan"?dstip=220.175.160.92?dstport=17048?dstintf="wan1"?dstintfrole="wan"?poluuid="0f219964-b02c-51e9-b295-bc4f936d8f3c"?sessionid=59239552?proto=17?action="accept"?policyid=1?policytype="policy"?service="udp/17048"?dstcountry="China"?srccountry="Reserved"?trandisp="snat"?transip=113.108.110.48?transport=16701?appcat="unknown"?applist="default"?duration=166?sentbyte=30?rcvdbyte=0?sentpkt=1?rcvdpkt=0?shapingpolicyid=9?shaperperipname="Limit-Wan1-15M"?shaperperipdropbyte=0?sentdelta=30?rcvddelta=0?mastersrcmac="1c:ab:34:9f:a9:fb"?srcmac="1c:ab:34:9f:a9:fb"?srcserver=0

      3)設(shè)計(jì)思路

      在使用KNN算法預(yù)測(cè)之前,我們首先要解決的是,如何使用這些日志數(shù)據(jù)來(lái)判別電腦的開關(guān)機(jī)情況?

      業(yè)務(wù)流程思考

      通過(guò)分析我們?nèi)粘?duì)電腦的操作,可以得出這些操作流程:

      早上上班 =》 打開電腦 =》 電腦軟件請(qǐng)求網(wǎng)絡(luò)訪問(wèn) =》 防火墻記錄請(qǐng)求日志 =》 晚上下班 =》 關(guān)閉電腦 =》 防火墻不再有這臺(tái)電腦的請(qǐng)求日志記錄

      業(yè)務(wù)問(wèn)題思考

      需要思考的問(wèn)題有:

      有時(shí)請(qǐng)假半天,只有半天的記錄

      請(qǐng)假一天,當(dāng)天沒(méi)有記錄

      電腦一直開著,沒(méi)有關(guān)機(jī)

      ip變更,有新機(jī)器加入網(wǎng)絡(luò)或原ip改變了

      節(jié)假日與工作日,電腦開機(jī)情況有很大差別

      移動(dòng)設(shè)備連接wifi如何區(qū)分

      虛擬機(jī)如何處理

      對(duì)于這些問(wèn)題,我們可以做如下處理:

      一天當(dāng)中,挑選出4個(gè)時(shí)間段,查看是否有這些ip日志記錄存在,有的則表示該時(shí)間段電腦處于開機(jī)狀態(tài),沒(méi)有的則表示處于關(guān)機(jī)狀態(tài)

      4個(gè)時(shí)間段可以選早上10點(diǎn)到11點(diǎn)、下午15點(diǎn)到16點(diǎn)、凌晨2點(diǎn)到3點(diǎn)、4點(diǎn)到5點(diǎn),白天只要有一條記錄,則表示電腦開機(jī)了,凌晨只要有一條記錄,則表示電腦沒(méi)關(guān)機(jī),而對(duì)于白天沒(méi)有記錄凌晨才有的,則記錄為沒(méi)關(guān)機(jī)狀態(tài)

      經(jīng)查看日志數(shù)據(jù)分析,srcmac記錄的并非是電腦網(wǎng)卡的mac,所以當(dāng)前只能用ip地址來(lái)判斷綁定電腦(如果防火墻能準(zhǔn)確獲取mac地址,則用mac判斷指定電腦狀態(tài)會(huì)更加準(zhǔn)確)

      需要設(shè)計(jì)ip表與狀態(tài)表兩個(gè)數(shù)據(jù)表,從日志中獲取到未記錄ip,需要先在ip表中進(jìn)行添加,然后再更新?tīng)顟B(tài)表

      每天需要為ip表記錄在狀態(tài)表里創(chuàng)建一條對(duì)應(yīng)的綁定記錄,因?yàn)閱T工請(qǐng)假后,當(dāng)天沒(méi)開機(jī)是沒(méi)有記錄的,除非不預(yù)測(cè)關(guān)機(jī)狀態(tài),只預(yù)測(cè)正常開關(guān)機(jī)與沒(méi)關(guān)機(jī)兩種狀態(tài)

      節(jié)假日與工作日的數(shù)據(jù)會(huì)有很大差別,需要作節(jié)假日判斷,進(jìn)行區(qū)分

      移動(dòng)設(shè)備如果在防火墻日志中區(qū)分不了,可使用獨(dú)立的交換機(jī)與網(wǎng)段進(jìn)行區(qū)分

      虛擬機(jī)無(wú)法區(qū)分,也當(dāng)作正常電腦來(lái)處理

      基于KNN算法,數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)思考

      針對(duì)KNN算法采用的是通過(guò)計(jì)算預(yù)測(cè)目標(biāo)與學(xué)習(xí)數(shù)據(jù)集每個(gè)數(shù)據(jù)的差值,找出K個(gè)差值最小的數(shù)據(jù),通過(guò)統(tǒng)計(jì)這些數(shù)據(jù)所屬類別哪一個(gè)占比較大,來(lái)決定預(yù)測(cè)目標(biāo)的類別方法。在做數(shù)據(jù)設(shè)計(jì)時(shí),我們需要將ip、節(jié)假日和工作日轉(zhuǎn)換為數(shù)值,這樣才能通過(guò)計(jì)算學(xué)習(xí),來(lái)判別指定ip在指定日期里,它的開關(guān)機(jī)狀態(tài)可能是哪一種。

      也就是說(shuō):我們需要將ip與節(jié)假日轉(zhuǎn)化為可以進(jìn)行加減運(yùn)行的數(shù)值,方便和預(yù)測(cè)目標(biāo)求差值,從而找出距離時(shí)最小的記錄。

      例如:我們將ip:192.168.10.10切分為4個(gè)數(shù)值,然后將它們分別乘于不同的256,計(jì)算得出一個(gè)唯一的數(shù)值,如:

      192 * 256 * 256 * 256 + 168 * 256 * 256 + 10 * 256 + 10 = 3232238090

      機(jī)器學(xué)習(xí)(06)——K近鄰算法實(shí)戰(zhàn)(k近鄰算法實(shí)驗(yàn))

      由于我們判斷的是局域網(wǎng)的電腦,而這些電腦的網(wǎng)段都是以192.168開頭的,所以我們只需要計(jì)算后面的差值即可:

      10 * 256 + 10 = 2570

      對(duì)于IP計(jì)算出來(lái)的值,為了區(qū)分每一個(gè)IP的變化狀態(tài),需要將結(jié)果再乘于500(到底乘多少需要根據(jù)其他參數(shù)值而定,只要能達(dá)到數(shù)據(jù)與數(shù)據(jù)的分隔就可以了),擴(kuò)大數(shù)值的差距(因?yàn)槊總€(gè)人的操作習(xí)性不一樣,IP差值太小時(shí),很容易在預(yù)測(cè)計(jì)算時(shí),發(fā)生越界,所求出差值最小的數(shù)據(jù)可能是多個(gè)不同ip的記錄。比如192.168.10.10與192.168.10.11之間,相差1,預(yù)測(cè)時(shí)它們分別與其他參數(shù)相加,有可能篩選出來(lái)的結(jié)果就會(huì)混雜在一塊)

      (10 * 256 + 10) * 500 = 1285000

      節(jié)假日與工作日,也可以轉(zhuǎn)換為0至6(即周一到周日)來(lái)進(jìn)行區(qū)分,對(duì)于周六、日等節(jié)假日,為了與工作日拉開距離,提升分析的準(zhǔn)確率,值都設(shè)置為7。

      而法定假期中,公歷假期可以直接通過(guò)日期進(jìn)行判斷,農(nóng)歷假期則可以調(diào)用相關(guān)插件,獲取農(nóng)歷日期來(lái)進(jìn)行判斷處理。對(duì)于節(jié)假日調(diào)休等情況,由于有更多的變數(shù)很難通過(guò)計(jì)算得知,對(duì)預(yù)測(cè)影響不大可以不作考慮。

      通過(guò)上面轉(zhuǎn)換,我們可以得出以下結(jié)果:

      #?日期??????IP???????????狀態(tài) 2020-01-01?192.168.10.10?沒(méi)開機(jī)???(周三,元旦) 2020-01-02?192.168.10.10?沒(méi)關(guān)機(jī)???(周四) 2020-01-03?192.168.10.10?正常?????(周五) 2020-01-04?192.168.10.10?沒(méi)開機(jī)???(周六) 2020-01-05?192.168.10.10?沒(méi)開機(jī)???(周日) 2020-01-06?192.168.10.10?正常?????(周一) 2020-01-05?192.168.10.14?沒(méi)開機(jī)???(周日) 2020-01-06?192.168.10.14?正常?????(周一) #?轉(zhuǎn)換結(jié)果為 #?日期?????IP值?周數(shù) 2020-01-01?1285000?7 2020-01-02?1285000?3 2020-01-03?1285000?4 2020-01-04?1285000?7 2020-01-05?1285000?7 2020-01-06?1285000?0 2020-01-06?1287000?7 2020-01-06?1287000?0

      當(dāng)我們要預(yù)測(cè)日期為2020-01-10,IP為192.168.10.10的開關(guān)機(jī)狀態(tài)時(shí),就可以先將預(yù)測(cè)參數(shù)先轉(zhuǎn)為對(duì)應(yīng)的數(shù)值,即2020-01-10是周五,即值為4,IP值為1285000,這兩個(gè)值做為參數(shù)代入KNN算法中進(jìn)行計(jì)算。

      #?計(jì)算結(jié)果(將學(xué)習(xí)數(shù)據(jù)集中每一條數(shù)據(jù)都與預(yù)測(cè)目標(biāo)相減,并將數(shù)據(jù)中的值求平方后相加————主要是為了去除負(fù)數(shù)) 相減后IP值?相減后周數(shù)值?相減后兩參數(shù)平方之和 0??????????3???????????9 0??????????-1??????????1 0??????????0???????????0 0??????????3???????????9 0??????????3???????????9 0??????????-4??????????16 2000?????????3???????????4000009 2000?????????-4??????????4000016

      通過(guò)從小到大排序,如果K值取1,則可以得出與目標(biāo)值最近的數(shù)據(jù)為2020-01-03 192.168.10.10 正常 (周五)這一條數(shù)據(jù),預(yù)測(cè)結(jié)果為“正常開關(guān)機(jī)”狀態(tài)。

      在做KNN預(yù)測(cè)時(shí),數(shù)據(jù)量越大預(yù)測(cè)結(jié)果越準(zhǔn)確,比如如果周五員工有8次正常開關(guān)機(jī),2次沒(méi)關(guān)機(jī),預(yù)測(cè)結(jié)果肯定為正常開關(guān)機(jī)狀態(tài),在概率上更靠近真實(shí)結(jié)果。

      而K的取值也是一樣,通過(guò)使用大量數(shù)據(jù)進(jìn)行測(cè)試,就可以找出預(yù)測(cè)成功率最高的區(qū)間,從而能更精確的進(jìn)行預(yù)測(cè)。

      在特征轉(zhuǎn)數(shù)值時(shí),有時(shí)候會(huì)遇到無(wú)法直接用數(shù)值代替的特征,可以使用索引或根據(jù)主觀判斷打分等方式進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換后需要使用大量數(shù)據(jù)對(duì)預(yù)測(cè)模型進(jìn)行測(cè)試,然后根據(jù)預(yù)測(cè)結(jié)果的準(zhǔn)確度進(jìn)行微調(diào),最終找到最優(yōu)的數(shù)值模型。

      ip表與狀態(tài)表設(shè)計(jì)

      ip表數(shù)據(jù)字典

      主要用來(lái)記錄當(dāng)前內(nèi)網(wǎng)所使用的ip,綁定使用人信息,以及按前面要求,將ip轉(zhuǎn)為數(shù)值,方便knn算法的計(jì)算使用

      狀態(tài)表數(shù)據(jù)字典

      狀態(tài)表記錄每天內(nèi)部電腦的通訊情況記錄,并根據(jù)這些記錄所判別的電腦狀態(tài)結(jié)果。同時(shí)也會(huì)記錄使用KNN算法進(jìn)行的預(yù)測(cè)結(jié)果,用于判斷預(yù)測(cè)成功率。

      3. 編碼實(shí)現(xiàn)

      編寫代碼實(shí)現(xiàn)前面設(shè)想的功能,需要分幾個(gè)步驟處理,首先要做的是數(shù)據(jù)清洗,從日志中將我們需要的數(shù)據(jù)提取出來(lái);然后對(duì)這些數(shù)據(jù)進(jìn)行加工處理,轉(zhuǎn)化為可能提供給機(jī)器學(xué)習(xí)算法使用的數(shù)據(jù);然后再是編碼算法代碼,實(shí)現(xiàn)預(yù)測(cè)操作。

      1)數(shù)據(jù)清洗

      日志每五分鐘會(huì)自動(dòng)進(jìn)行切割,生成新的日志文件,可以定時(shí)(前面所指定的檢查時(shí)間)對(duì)日志文件進(jìn)行批量檢查處理。

      本文主要是介紹根據(jù)KNN算法實(shí)現(xiàn)項(xiàng)目功能,所以略過(guò)數(shù)據(jù)清洗等功能實(shí)現(xiàn)。

      針對(duì)我們想要實(shí)現(xiàn)的功能,我們只需要在固定時(shí)間段從日志中提取該時(shí)間內(nèi)請(qǐng)求的所有ip即可。可以直接從日志文件中提取,也可以使用Flume+Kafka+HBase方式,將日志數(shù)據(jù)從各系統(tǒng)中收集整理好,再?gòu)腍Base中獲取。

      #?10點(diǎn)防火墻請(qǐng)求ip集 192.168.10.38,192.168.20.30,192.168.20.23...192.168.20.34,192.168.20.41 #?15點(diǎn)防火墻請(qǐng)求ip集 192.168.10.28,192.168.10.23,192.168.10.26...192.168.20.9,192.168.20.15 #?第二天凌晨2點(diǎn)防火墻請(qǐng)求ip集 193.192.168.10.104,192.168.10.97,192.168.10.93...192.168.10.95,192.168.10.90 #?第二天凌晨4點(diǎn)防火墻請(qǐng)求ip集 192.168.10.96,192.168.10.66,192.168.20.89...192.168.10.57,192.168.10.73

      2)數(shù)據(jù)加工

      ip前面雖然已經(jīng)提取出來(lái)了,但還需要將它們更新到數(shù)據(jù)庫(kù)中,方便后續(xù)KNN算法的調(diào)用。所以需要實(shí)現(xiàn)一個(gè)狀態(tài)更新服務(wù),將清洗好的數(shù)據(jù),更新到ip表與狀態(tài)表中。

      主要思路是:

      開發(fā)一個(gè)pc狀態(tài)定時(shí)更新服務(wù),該服務(wù)在指定時(shí)間啟動(dòng),讀取清洗好的數(shù)據(jù)(ip列表),將這些ip記錄更新到ip表中存儲(chǔ)起來(lái)(已存在則不操作,不存在則添加)

      在狀態(tài)表為每一個(gè)ip創(chuàng)建一條對(duì)應(yīng)的記錄,日期為當(dāng)天的時(shí)間。這主要是為了防止有些人請(qǐng)假或節(jié)假日關(guān)機(jī),對(duì)應(yīng)的ip沒(méi)有請(qǐng)求日志,會(huì)被忽略掉。創(chuàng)建記錄時(shí),默認(rèn)電腦實(shí)際狀態(tài)為關(guān)機(jī)狀態(tài)。

      獲取ip對(duì)應(yīng)的id值,然后根據(jù)執(zhí)行的時(shí)間,如果是白天則直接更新?tīng)顟B(tài)表中對(duì)應(yīng)字段的狀態(tài)值,表示該電腦已啟動(dòng)。如果是凌晨有記錄,則表示該電腦一直未關(guān)機(jī),將電腦狀態(tài)更新為沒(méi)關(guān)機(jī)狀態(tài)。

      """ 每天定時(shí)更新pc開關(guān)機(jī)狀態(tài)服務(wù) """ import?logging import?os import?sys import?json import?datetime import?zhdate from?common?import?log_helper,?datetime_helper,?hbase_helper,?convert_helper from?common.string_helper?import?string from?config?import?const from?logic?import?pc_info_logic,?pc_on_off_state_logic #?獲取本腳本所在的路徑 pro_path?=?os.path.split(os.path.realpath(__file__))[0] sys.path.append(pro_path) #?定義日志輸出格式 logging.basicConfig(level=logging.INFO, ????????????????????format='%(asctime)s?%(filename)s[line:%(lineno)d]?%(levelname)s?%(message)s', ????????????????????filename=const.SERVICE_LOG_FILE_PATH?+?"/pc_on_off_state_update_service.log", ????????????????????filemode='a') #?定義數(shù)據(jù)表名稱 table?=?'firewall' def?update_state(update_date,?check_time): ????""" ????更新pc開關(guān)機(jī)狀態(tài) ????:param?update_date:?更新?tīng)顟B(tài)的日期時(shí)間。需要判斷是否是凌晨時(shí)間,凌晨是否開關(guān)機(jī)狀態(tài),需要同步的是昨天電腦狀態(tài)數(shù)據(jù),即下班后電腦開關(guān)機(jī)狀態(tài) ????:param?check_time:?狀態(tài)檢查時(shí)間為:早上10點(diǎn),下午的15點(diǎn)和第二天凌晨2點(diǎn)與4點(diǎn) ????:return: ????""" ????#?記錄服務(wù)啟動(dòng)時(shí)間 ????start_time?=?datetime.datetime.now() ????run_time_log?=?'\n-----------------------------------\n' ????run_time_log?=?run_time_log?+?'開始:'?+?str(start_time)?+?'\n' ????with?hbase_helper.Hbase()?as?hbase: ????????#?從hbase中讀取指定時(shí)間已處理好(已清洗)的分析匯總信息 ????????result?=?hbase.get(table,?'summary:hour,{},60'.format(check_time)) ????????if?not?result: ????????????log_helper.info('summary:hour,{},60?數(shù)據(jù)不存在'.format(check_time),?True) ????????????return ????????#?獲取各防火墻ip訪問(wèn)統(tǒng)計(jì)數(shù)據(jù),并轉(zhuǎn)為json格式 ????????data?=?json.loads(result['log_analysis:data']) ????????#?提取研發(fā)部該時(shí)間段內(nèi)所有記錄在防火墻日志中的ip數(shù)據(jù),并過(guò)濾掉非研發(fā)部的其他ip數(shù)據(jù) ????????#?['192.168.10.38','192.168.20.30','192.168.20.23'...'192.168.20.34','192.168.20.41'] ????????ips?=?[ip?for?dev?in?data?for?ip?in?data[dev]?if?'192.168.10.'?in?ip?or?'192.168.20.'?in?ip] ????????#?對(duì)ip數(shù)據(jù)進(jìn)行去重操作 ????????ips?=?set(ips) ????????#?判斷日期是否是工作日,并返回對(duì)應(yīng)日期的值 ????????weekdays?=?get_weekdays(update_date) ????????#?初始化狀態(tài)表邏輯類 ????????_pc_on_off_state_logic?=?pc_on_off_state_logic.PcOnOffStateLogic() ????????#?檢查當(dāng)天的狀態(tài)數(shù)據(jù)是否已生成,未生成則進(jìn)行批量添加操作 ????????if?not?_pc_on_off_state_logic.exists('date=\'{}\''.format(update_date)): ????????????_pc_on_off_state_logic.execute('insert?into?pc_on_off_state(date,?pc_info_id,?weekdays)?select?\'{}\',?id,?{}?from?pc_info'.format(update_date,?weekdays)) ????????#?初始化ip表邏輯類 ????????_pc_info_logic?=?pc_info_logic.PcInfoLogic() ????????#?遍歷所有ip,并對(duì)這些ip進(jìn)行相關(guān)的處理操作 ????????for?ip?in?ips: ????????????#?檢查當(dāng)前ip是否已添加到ip表,不存在的則進(jìn)行添加操作 ????????????model?=?_pc_info_logic.get_model_for_cache_of_where('ip=\'{}\''.format(ip)) ????????????if?not?model: ????????????????#?將ip轉(zhuǎn)為數(shù)值 ????????????????_ip?=?ip.split('.') ????????????????ip_num?=?(convert_helper.to_int0(_ip[2])?*?256?+?convert_helper.to_int0(_ip[3]))?*?500 ????????????????#?組合ip表更新數(shù)據(jù) ????????????????fields?=?{ ????????????????????'ip':?string(ip), ????????????????????'ip_num':?ip_num ????????????????} ????????????????#?添加新ip記錄 ????????????????model?=?_pc_info_logic.add_model(fields,?returning='*') ????????????????#?狀態(tài)表也添加一條新的ip記錄 ????????????????_pc_on_off_state_logic.add_model({'date':?string(update_date),?'pc_info_id':?model.get('id')}) ????????????#?組合狀態(tài)表更新數(shù)據(jù) ????????????fields?=?{ ????????????????'date':?string(update_date), ????????????????'pc_info_id':?model.get('id'), ????????????????'weekdays':?weekdays, ????????????????'state':?string('normal')???#?只要有ip請(qǐng)求,即表示該電腦當(dāng)開已開機(jī),設(shè)置它的默認(rèn)值為正常開關(guān)機(jī)狀態(tài) ????????????} ????????????#?通過(guò)判斷當(dāng)前檢查時(shí)間,來(lái)同步更新各時(shí)間段的狀態(tài) ????????????if?check_time.hour?==?10: ????????????????fields['ten_points_state']?=?1 ????????????elif?check_time.hour?==?15: ????????????????fields['quinze_state']?=?1 ????????????#?凌晨只要有一條請(qǐng)求記錄,就表示這臺(tái)電腦沒(méi)有關(guān)機(jī) ????????????elif?check_time.hour?==?2: ????????????????fields['two_points_state']?=?1 ????????????????fields['state']?=?string('on')??#?設(shè)置為沒(méi)關(guān)機(jī)狀態(tài) ????????????elif?check_time.hour?==?4: ????????????????fields['four_points_state']?=?1 ????????????????fields['state']?=?string('on') ????????????#?更新?tīng)顟B(tài)表數(shù)據(jù) ????????????_pc_on_off_state_logic.edit(fields,?'date=\'{}\'?and?pc_info_id={}'.format(update_date,?model.get('id'))) ????#?記錄程序運(yùn)行結(jié)束時(shí)間 ????run_time_end?=?datetime.datetime.now() ????run_time_log?=?run_time_log?+?'服務(wù)執(zhí)行結(jié)束:'?+?str(run_time_end)?+?'\n' ????run_time_log?=?run_time_log?+?'總用時(shí):'?+?str(run_time_end?-?start_time)?+?'\n' ????run_time_log?=?run_time_log?+?'-----------------------------------\n' ????print(run_time_log) def?get_weekdays(date): ????"""判斷日期是否是工作日,并返回對(duì)應(yīng)標(biāo)識(shí)""" ????if?is_holidays(date): ????????return?7 ????elif?date.weekday()?>=?5: ????????return?7 ????return?date.weekday() def?is_holidays(date): ????""" ????判斷是否為節(jié)假日 ????:param?date:?需要檢測(cè)的日期 ????:return:?返回True或False ????""" ????#?判斷是否是陽(yáng)歷的節(jié)假日(元旦、五一、十一) ????if?(date.month?==?1?and?date.day?==?1)?or?(date.month?==?5?and?date.day?==?1)?or?\ ????????????(date.month?==?10?and?(date.day?>=?1?or?date.day?<=7)): ????????return?True ????#?使用zhdate包,將陽(yáng)歷日期轉(zhuǎn)換成農(nóng)歷日期對(duì)象 ????lunar_calendar?=?zhdate.ZhDate.from_datetime(datetime.datetime(date.year,?date.month,?date.day)) ????#?檢查是否是過(guò)年、端午節(jié)和中秋節(jié),清明暫時(shí)無(wú)法計(jì)算出來(lái),不做判斷 ????lunar_calendar?=?lunar_calendar.chinese() ????if?'臘月二十八'?in?lunar_calendar?or?'臘月二十九'?in?lunar_calendar?or?'臘月三十'?in?lunar_calendar?or?\ ????????????'正月初一'?in?lunar_calendar?or?'正月初二'?in?lunar_calendar?or?'正月初三'?in?lunar_calendar?or?\ ????????????'正月初四'?in?lunar_calendar?or?'正月初五'?in?lunar_calendar?or?'正月初六'?in?lunar_calendar?or?\ ????????????'五月初五'?in?lunar_calendar?or?'八月十五'?in?lunar_calendar: ????????return?True ????return?False if?__name__?==?'__main__': ????###?接收參數(shù)?### ????if?len(sys.argv)?

      更新后數(shù)據(jù)表結(jié)果

      3)使用KNN算法實(shí)現(xiàn)預(yù)測(cè)功能

      從數(shù)據(jù)表中獲取機(jī)器學(xué)習(xí)數(shù)據(jù)

      #?初始化ip表與狀態(tài)表邏輯類 ????_pc_info_logic?=?pc_info_logic.PcInfoLogic() ????_pc_on_off_state_logic?=?pc_on_off_state_logic.PcOnOffStateLogic() ????#?組合sql查詢語(yǔ)句,獲取最近2個(gè)月的分析數(shù)據(jù)作為機(jī)器學(xué)習(xí)數(shù)據(jù) ????#?select?pc_info.id,ip,ip_num,weekdays,state?from?pc_info?left?join?pc_on_off_state?on?pc_info.id=pc_on_off_state.pc_info_id?where?'2019-11-12'<=date?and?date<'2020-01-11' ????sql?=?""" ????????select?pc_info.id,ip,ip_num,weekdays,state ????????from?pc_info?left?join?pc_on_off_state?on?pc_info.id=pc_on_off_state.pc_info_id ????????where?'{}'<=date?and?date<'{}' ????""".format(datetime_helper.timedelta('d',?date,?-60),?datetime_helper.to_today()) ????#?提交查詢,獲取列表數(shù)據(jù) ????#?[{'id':?101,?'ip':?'192.168.10.19',?'ip_num':?1289500,?'weekdays':?2,?'state':?'on'},?{'id':?100,?'ip':?'192.168.20.2',?'ip_num':?2561000,?'weekdays':?2,?'state':?'on'},?...] ????result?=?_pc_info_logic.select(sql) ???? ????#?從查詢結(jié)果列表中,提取ip_num與weekdays兩個(gè)字段值,并組成list ????#?[[1289500,?2],?[2561000,?2],?...] ????ml_data?=?[[item['ip_num'],?item['weekdays']]?for?item?in?result] ????#?將最后一列值存儲(chǔ)到標(biāo)簽集中(特征所對(duì)應(yīng)的答案) ????#?['on',?'on',?'on',?'normal',?...?] ????ml_label?=?[item['state']?for?item?in?result] ???? ????#?將數(shù)組轉(zhuǎn)換為numpy數(shù)組,得到機(jī)器學(xué)習(xí)數(shù)據(jù)矩陣ml_data ????#?[[1289500,?2] ????#??[2561000,?2] ????#??[1355000,?2] ????#??...] ????ml_data?=?numpy.array(ml_data) ????#?將數(shù)組中的值由字符串轉(zhuǎn)為浮點(diǎn)型(int型數(shù)值如果不進(jìn)行歸一化處理,當(dāng)數(shù)比較大時(shí)執(zhí)行平方操作,會(huì)讓值溢出越界,正數(shù)值變成負(fù)數(shù)值,產(chǎn)生錯(cuò)誤) ????#?[[1.2895e+06,?2.0000e+00] ????#??[2.5610e+06,?2.0000e+00] ????#??[1.3550e+06,?2.0000e+00] ????#??...] ????ml_data?=?ml_data.astype(float) ???? ????#?到此,我們已獲得機(jī)器學(xué)習(xí)需要使用到的數(shù)據(jù)矩陣ml_label以及對(duì)應(yīng)的標(biāo)簽(答案)ml_label

      對(duì)每個(gè)ip分別進(jìn)行預(yù)測(cè)操作

      從ip表中讀取全部ip數(shù)據(jù),對(duì)每個(gè)ip分別進(jìn)行預(yù)測(cè),將預(yù)測(cè)結(jié)果更新到狀態(tài)表中

      #?獲取當(dāng)前日期 ????date?=?datetime.datetime.now().date() ????#?檢查當(dāng)前時(shí)間是否為節(jié)假日 ????weekdays?=?get_weekdays(date) ????#?添加當(dāng)天需要預(yù)測(cè)與記錄的數(shù)據(jù) ????if?not?_pc_on_off_state_logic.exists('date=\'{}\''.format(date)): ????????_pc_on_off_state_logic.execute('insert?into?pc_on_off_state(date,?pc_info_id,?weekdays)?select?\'{}\',?id,?{}?from?pc_info'.format(date,?weekdays)) ????#?從ip表中讀取全部ip數(shù)據(jù) ????result?=?_pc_info_logic.get_list(is_return_list=True) ????#?對(duì)每個(gè)ip分別進(jìn)行預(yù)測(cè) ????for?item?in?result: ????????#?獲取id、ip和ip值參數(shù) ????????id?=?item.get('id') ????????ip?=?item.get('ip') ????????ip_num?=?item.get('ip_num') ????????#?組合成預(yù)測(cè)參數(shù) ????????#?[1.299e+06,?7.000e+00] ????????check_data?=?numpy.array([ip_num,?weekdays]) ????????check_data?=?check_data.astype(float) ????????#?進(jìn)行預(yù)測(cè)操作 ????????label?=?knn_helper.knn_classify(ml_data,?ml_label,?check_data,?9) ????????#?組合更新字段,更新預(yù)測(cè)結(jié)果 ????????fields?=?{ ????????????'calculate':?string(label) ????????} ????????_pc_on_off_state_logic.edit(fields,?'date=\'{}\'?and?pc_info_id={}'.format(date,?id))

      KNN算法實(shí)現(xiàn)

      下面有兩種完成KNN算法的代碼,方法一利用python的特性簡(jiǎn)化的代碼,方法二是對(duì)算法進(jìn)行拆解說(shuō)明的方法,代碼實(shí)現(xiàn)主要參考:?https://github.com/apachecn/AiLearning/blob/master/docs/ml/2.k-近鄰算法.md?文檔

      #?實(shí)現(xiàn)方法一 def?knn_classify(ml_data,?ml_label,?test_data,?k): ????""" ????kNN分類算法函數(shù) ????:param?ml_data:?訓(xùn)練數(shù)據(jù)特征集(features) ????:param?ml_label:?訓(xùn)練數(shù)據(jù)特征標(biāo)簽集(labels————特征集答案) ????:param?test_data:?用于knn分類測(cè)試的數(shù)據(jù) ????:param?k:?選擇最近鄰的數(shù)目 ????:return:?返回knn算法預(yù)測(cè)的結(jié)果(所對(duì)應(yīng)的標(biāo)簽值————分類值label) ????""" ????#?讓預(yù)測(cè)參數(shù)矩陣(test_data)對(duì)每一個(gè)訓(xùn)練集矩陣(ml_data)相減,并求平方值(將負(fù)數(shù)轉(zhuǎn)為正數(shù)), ????#?然后對(duì)矩陣中的值執(zhí)行求和運(yùn)算,得出每個(gè)訓(xùn)練集矩陣數(shù)據(jù)與預(yù)測(cè)參數(shù)矩陣的距離值 ????distances?=?numpy.sum((test_data?-?ml_data)?**?2,?axis=1) ????#?將矩陣距離值(distances)從小到大排序,并提取其對(duì)應(yīng)的index(索引),然后用索引值生成新的矩陣 ????#?只取出排在前k位的索引值,用于ml_label提取對(duì)應(yīng)的標(biāo)簽 ????labels?=?[ml_label[index]?for?index?in?distances.argsort()[0:?k]] ????#?使用Counter函數(shù)統(tǒng)計(jì)列表(labels)中,各標(biāo)簽出現(xiàn)的次數(shù),并按從大到小排列, ????#?然后返回標(biāo)簽數(shù)最多的元素,將這個(gè)元素的標(biāo)簽返回給調(diào)用程序 ????return?Counter(labels).most_common(1)[0][0] #?實(shí)現(xiàn)方法二 def?knn_classify2(ml_data,?ml_label,?test_data,?k): ????""" ????kNN分類算法函數(shù) ????:param?ml_data:?訓(xùn)練數(shù)據(jù)特征集(features) ????:param?ml_label:?訓(xùn)練數(shù)據(jù)特征標(biāo)簽集(labels————特征集答案) ????:param?test_data:?用于knn分類測(cè)試的數(shù)據(jù) ????:param?k:?選擇最近鄰的數(shù)目 ????:return:?返回knn算法預(yù)測(cè)的結(jié)果(所對(duì)應(yīng)的標(biāo)簽值————分類值label) ????""" ????###?1.?距離計(jì)算 ????#?獲取訓(xùn)練集數(shù)據(jù)大小 ????data_size?=?ml_data.shape[0] ????#?使用numpy的tile函數(shù),生成和訓(xùn)練樣本對(duì)應(yīng)的矩陣,并與訓(xùn)練樣本求差 ????""" ????tile會(huì)將第一參數(shù)中的數(shù)組復(fù)制成指定數(shù)量的矩陣 ????比如:numpy.tile(test_data,?(10,?1)) ????????test_data?=?[1.299e+06,?7.000e+00] ????????當(dāng)?shù)诙€(gè)參數(shù)為(5,?1)時(shí),則表示會(huì)將創(chuàng)建一個(gè)行數(shù)為10的1維數(shù)組集,每一行等于test_data值的矩陣 ????????即: ????????result?=?[[1.299e+06,?7.000e+00], ??????????????????[1.299e+06,?7.000e+00], ??????????????????[1.299e+06,?7.000e+00], ??????????????????[1.299e+06,?7.000e+00], ??????????????????[1.299e+06,?7.000e+00]] ????numpy.tile(test_data,?(data_size,?1)) ????則會(huì)用test_data數(shù)據(jù)生成一個(gè)與ml_data一樣大小的一個(gè)矩陣,用于與ml_data進(jìn)行運(yùn)算 ????""" ????data_tile?=?numpy.tile(test_data,?(data_size,?1)) ????#?將測(cè)試數(shù)據(jù)test_data生成的矩陣與訓(xùn)練數(shù)據(jù)特征集數(shù)據(jù)ml_data相減,求兩者的不同點(diǎn) ????""" ????比如訓(xùn)練集矩陣 ????ml_data?=?[[1.2895e+06,?2.0000e+00] ???????????????[2.5610e+06,?2.0000e+00] ???????????????[1.3550e+06,?2.0000e+00] ???????????????[1.3615e+06,?2.0000e+00] ???????????????[2.5720e+06,?2.0000e+00] ???????????????[1.2815e+06,?2.0000e+00] ???????????????...] ????data_tile?-?ml_data?=?[[?9.5000e+03,?5.0000e+00] ???????????????????????????[-1.2620e+06,?5.0000e+00] ???????????????????????????[-5.6000e+04,?5.0000e+00] ???????????????????????????[-6.2500e+04,?5.0000e+00] ???????????????????????????[-1.2730e+06,?5.0000e+00] ???????????????????????????[?1.7500e+04,?5.0000e+00] ???????????????????????????...] ????""" ????diff_mat?=?data_tile?-?ml_data ????#?矩陣相減計(jì)算出的結(jié)果求平方值 ????#?通過(guò)前面兩個(gè)矩陣求差值后,得出的矩陣中的值有可能為負(fù)數(shù),求平方是讓結(jié)果全都變?yōu)檎龜?shù),方便后面對(duì)結(jié)果進(jìn)行比較與排序 ????""" ????#?對(duì)矩陣?yán)锏拿總€(gè)值都求平方,這些值可能會(huì)有點(diǎn)大,可以在前面做歸一化處理,讓這些值變小 ????diff_mat_square?=?[[9.02500000e+07,?2.50000000e+01] ???????????????????????[1.59264400e+12,?2.50000000e+01] ???????????????????????[3.13600000e+09,?2.50000000e+01] ???????????????????????[3.90625000e+09,?2.50000000e+01] ???????????????????????[1.62052900e+12,?2.50000000e+01] ???????????????????????[3.06250000e+08,?2.50000000e+01] ???????????????????????...] ????""" ????diff_mat_square?=?numpy.square(diff_mat) ????#?將矩陣的每一行相加 ????""" ????將預(yù)測(cè)參數(shù)矩陣與訓(xùn)練集矩陣求差求平方后,得出的結(jié)果再相加,這樣就可以計(jì)算出預(yù)測(cè)參數(shù)與訓(xùn)練集中每個(gè)數(shù)據(jù)的距離(差距)值了,差距值越小,就表示與預(yù)測(cè)結(jié)果越相似 ????distances?=?[9.02500250e+07,?1.59264400e+12,?3.13600002e+09?...] ????""" ????distances?=?diff_mat_square.sum(axis=1) ????#?根據(jù)距離排序從小到大的排序,返回對(duì)應(yīng)的索引位置 ????""" ????argsort()?會(huì)將矩陣(distances)中的值從小到大排列,并提取其對(duì)應(yīng)的index(索引),然后用索引值生成新的矩陣 ????例如: ????????y?=?numpy.array([8,0,52,7,66,21,36]) ????????矩陣y使用argsort函數(shù)進(jìn)行排序后,值為其值的索引組成的矩陣 ????????y.argsort()?=?numpy.array([1,3,0,5,6,2,4]) ????????表示的是y[1]?

      預(yù)測(cè)后數(shù)據(jù)表結(jié)果

      3. KNN算法參數(shù)調(diào)優(yōu)

      完成開發(fā)以后,預(yù)測(cè)的準(zhǔn)確率并不一定是最高的,需要調(diào)整算法的K值參數(shù),以及對(duì)數(shù)據(jù)進(jìn)行優(yōu)化調(diào)整,才可能提升預(yù)測(cè)成功率。

      K值的調(diào)整比較簡(jiǎn)單,只需要寫一個(gè)腳本,通過(guò)調(diào)整K值的大小,對(duì)歷史數(shù)據(jù)進(jìn)行預(yù)測(cè),并將預(yù)測(cè)結(jié)果與實(shí)際結(jié)果進(jìn)行比較,計(jì)算出預(yù)測(cè)的準(zhǔn)確率,然后匯總所有預(yù)測(cè)準(zhǔn)確率計(jì)算其平均值,得出不同K值情況下,使用不同數(shù)量機(jī)器訓(xùn)練數(shù)據(jù)所預(yù)測(cè)出來(lái)的準(zhǔn)確率,從中找出最優(yōu)預(yù)測(cè)結(jié)果的參數(shù),數(shù)據(jù)項(xiàng)與K值參數(shù)來(lái)使用。

      對(duì)于數(shù)據(jù),需要對(duì)加工后的數(shù)據(jù)(KNN訓(xùn)練數(shù)據(jù)集)進(jìn)行檢查,查看里面的數(shù)據(jù)是否準(zhǔn)確,是否存在問(wèn)題,算法與規(guī)則是否符合要求。

      比如這個(gè)例子中,weekdays值的變化,對(duì)預(yù)測(cè)結(jié)果有什么樣的影響?對(duì)于節(jié)假日調(diào)休,對(duì)長(zhǎng)期的預(yù)測(cè)準(zhǔn)確率會(huì)有什么影響?ip轉(zhuǎn)為int值后是否需要將ip之間拉開距離?拉開ip值與值距離的參數(shù)怎么設(shè)置?用10、50、100等值,會(huì)造成什么樣的影響?為什么要用500?如果預(yù)測(cè)參數(shù)由兩個(gè)變?yōu)楦鄷r(shí),這個(gè)值應(yīng)該怎么設(shè)置?訓(xùn)練數(shù)據(jù)混雜在一塊,對(duì)測(cè)試結(jié)果有什么影響?是否需要故意混雜這些數(shù)據(jù),用于計(jì)算最近距離的數(shù)據(jù)有更多的可能性?在代碼運(yùn)行過(guò)程中,也需要通過(guò)debug或打印結(jié)果的方式進(jìn)行查看分析,比如不使用浮點(diǎn)類型和歸一化矩陣數(shù)據(jù)時(shí),會(huì)有什么樣的影響?對(duì)預(yù)測(cè)準(zhǔn)確率有什么影響?如果一個(gè)ip使用很長(zhǎng)時(shí)間后,不再使用或者某種習(xí)慣改變了,對(duì)于預(yù)測(cè)結(jié)果有什么樣的影響?應(yīng)該如何優(yōu)化?參數(shù)應(yīng)該如何設(shè)置才更加合理?

      在算法參數(shù)調(diào)優(yōu)時(shí),需要多開動(dòng)腦筋,多觀察多思考多問(wèn)為什么,這樣才能及時(shí)發(fā)現(xiàn)問(wèn)題,并對(duì)問(wèn)題進(jìn)行修正,多動(dòng)手測(cè)試數(shù)據(jù),才能找出最優(yōu)的參數(shù)設(shè)置。

      4. 其他例子

      驗(yàn)證碼識(shí)別與約會(huì)數(shù)據(jù)學(xué)習(xí)例子代碼:

      Knn算法例子

      5. 參考資料

      https://github.com/apachecn/AiLearning/blob/master/docs/ml/2.k-近鄰算法.md

      https://baike.baidu.com/item/k近鄰算法/9512781?fr=aladdin

      附件: knn.zip 444.52KB 下載次數(shù):0次

      機(jī)器學(xué)習(xí)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:我從來(lái)沒(méi)有想到原來(lái)的“Excel列”功能如此強(qiáng)大
      下一篇:如何在電腦進(jìn)行共享文件表格(文件表格怎么共享)
      相關(guān)文章
      国产亚洲精品福利在线无卡一| 亚洲av日韩专区在线观看| 亚洲日本韩国在线| 亚洲日韩久久综合中文字幕| 亚洲宅男天堂a在线| 亚洲午夜在线电影| 亚洲爆乳无码一区二区三区| 国产AV无码专区亚洲AV手机麻豆| 亚洲精品第一国产综合精品99| 亚洲成a人片在线播放| yy6080亚洲一级理论| 精品久久久久久亚洲综合网| 久久精品国产亚洲AV| 亚洲国产精品国产自在在线 | 亚洲国产精品日韩在线观看| 亚洲欧洲校园自拍都市| 亚洲国产成人久久三区| 亚洲三级高清免费| 亚洲日本VA中文字幕久久道具| 亚洲日韩精品国产一区二区三区| 亚洲精品无码一区二区 | 亚洲国产精品18久久久久久 | 亚洲第一成年男人的天堂| 亚洲视频在线观看免费| 久久精品国产亚洲AV电影| 亚洲黄色一级毛片| 亚洲av永久无码精品三区在线4 | 亚洲国产视频网站| 国产成人精品日本亚洲专区6| 亚洲1234区乱码| 国产午夜亚洲精品国产| 亚洲乱码国产乱码精华| 国产精品亚洲专区在线播放| 亚洲免费在线观看| 亚洲精品自在在线观看| 亚洲人成电影在线天堂| 亚洲成人免费网站| 亚洲精品无码久久久久APP| 亚洲av无码偷拍在线观看| 国产精品自拍亚洲| 中文字幕精品亚洲无线码二区|