隨機森林之特征選擇
摘要:在隨機森林介紹中提到了隨機森林一個重要特征:能夠計算單個特征變量的重要性。並且這一特征在很多方面能夠得到應用,例如在銀行貸款業務中能否正確的評估一個企業的信用度,關系到是否能夠有效地回收貸款。但是信用評估模型的數據特征有很多,其中不乏有很多噪音,所以需要計算出每一個特征的重要性並對這些特征進行一個排序,進而可以從所有特征中選擇出重要性靠前的特征。
一:特征重要性
在隨機森林中某個特征X的重要性的計算方法如下:
1:對於隨機森林中的每一顆決策樹,使用相應的OOB(袋外數據)數據來計算它的袋外數據誤差,記為errOOB1.
2: 隨機地對袋外數據OOB所有樣本的特征X加入噪聲干擾(就可以隨機的改變樣本在特征X處的值),再次計算它的袋外數據誤差,記為errOOB2.
3:假設隨機森林中有Ntree棵樹,那么對於特征X的重要性=∑(errOOB2-errOOB1)/Ntree,之所以可以用這個表達式來作為相應特征的重要性的度量值是因為:若給某個特征隨機加入噪聲之后,袋外的准確率大幅度降低,則說明這個特征對於樣本的分類結果影響很大,也就是說它的重要程度比較高。
二:特征選擇
在論文 Variable Selection using Random Forests中詳細的論述了基於隨機森林的特征選擇方法,這里我們進行一些回顧。
首先特征選擇的目標有兩個:
1:找到與應變量高度相關的特征變量。
2:選擇出數目較少的特征變量並且能夠充分的預測應變量的結果。
其次一般特征選擇的步驟為:
1:初步估計和排序
a)對隨機森林中的特征變量按照VI(Variable Importance)降序排序。
b)確定刪除比例,從當前的特征變量中剔除相應比例不重要的指標,從而得到一個新的特征集。
c)用新的特征集建立新的隨機森林,並計算特征集中每個特征的VI,並排序。
d)重復以上步驟,直到剩下m個特征。
2:根據1中得到的每個特征集和它們建立起來的隨機森林,計算對應的袋外誤差率(OOB err),將袋外誤差率最低的特征集作為最后選定的特征集。
地址轉換協議ARP
在以太網協議中規定,同一局域網中的一台主機要和另一台主機進行直接通信,必須要知道目標主機的MAC地址。而在TCP/IP協議中,網絡層和傳輸層只關心目標主機的IP地址。這就導致在以太網中使用IP協議時,數據鏈路層的以太網協議接到上層IP協議提供的數據中,只包含目的主機的IP地址。於是需要一種方法,根據目的主機的IP地址,獲得其MAC地址。這就是ARP協議要做的事情。
所謂地址解析(address resolution)就是主機在發送幀前將目標IP地址轉換成目標MAC地址的過程。
理論結構
ARP軟件可分為三部分:
- 輸出模塊
- 將高層協議地址與相應的物理地址進行綁定,返回給網絡接口程序
- 輸入模塊
- 處理來自網絡的ARP分組,並通過增加新的綁定來修改ARP高速緩存的內容
- 高速緩存管理程序
- 實現高速緩存替換策略;檢測高速緩存中的所有表項,刪除已達到規定時限的表項
輸出模塊
該模塊主要是要接收IP數組請求,然后查找物理地址,返回。
主要步驟為:
1. 睡眠,直到IP軟件收到IP分組。
2. 檢查高速緩存表,尋找對應於這個IP分組的項目。
3. if ( 找到 ){
if ( 狀態為 RESOLVED ){
提取硬件物理地址;
將分組連同硬件物理地址一起發送到數據鏈路層;
return;
}
else if ( 狀態為 PENDING ){
將分組放入相應的隊列;
return;
}
}
else{
創建一個隊列;
將分組加入到隊列中;
創建一個高速緩存項目,狀態設置為 PENDING ,ATTEMPTS 為 1;
發送ARP請求;
}
ARP高速緩存隊列
它是用數組來存儲的。
extern struct arpentry arptable[ARP_TSIZE]
搜索ARP高速緩存
/* arpfind.c - arpfind */ #include <conf.h> #include <kernel.h> #include <network.h> /*------------------------------------------------------------------------ * arpfind - find an ARP entry given a protocol address and interface *------------------------------------------------------------------------ */ struct arpentry * arpfind(u_char *pra, u_short prtype, struct netif *pni) { struct arpentry *pae; /* 定義ARP緩存結構體指針 */ int i; for (i=0; i<ARP_TSIZE; ++i) { /* 遍歷ARP高速緩存 */ pae = &arptable[i]; /* 高速緩存數組 */ if (pae->ae_state == AS_FREE) /* 緩存為空閑接找下一個 */ continue; if (pae->ae_prtype == prtype && /* 協議類型相同 */ pae->ae_pni == pni && /* 接口和協議地址相同 */ BLKEQU(pae->ae_pra, pra, pae->ae_prlen)) /* BLKEQU的定義 #define BLKEQU(b1, b2, len) (!memcmp((b1), (b2), len))*/ return pae; } return 0; }
ARP請求分組的廣播
/* arpsend.c - arpsend */ #include <conf.h> #include <kernel.h> #include <network.h> /*------------------------------------------------------------------------ * arpsend - broadcast an ARP request * N.B. Assumes interrupts disabled *------------------------------------------------------------------------ */ int arpsend(pae) struct arpentry *pae; //指向高速緩存的表項 { struct netif *pni = pae->ae_pni; struct ep *pep; struct arp *parp; int arplen; pep = (struct ep *) getbuf(Net.netpool); //生成ARP請求分組 if ((int)pep == SYSERR) return SYSERR; blkcopy(pep->ep_dst, pni->ni_hwb.ha_addr, pae->ae_hwlen); pep->ep_type = EPT_ARP; pep->ep_order = EPO_NET; parp = (struct arp *) pep->ep_data; parp->ar_hwtype = hs2net(pae->ae_hwtype); parp->ar_prtype = hs2net(pae->ae_prtype); parp->ar_hwlen = pae->ae_hwlen; parp->ar_prlen = pae->ae_prlen; parp->ar_op = hs2net(AR_REQUEST); blkcopy(SHA(parp), pni->ni_hwa.ha_addr, pae->ae_hwlen); blkcopy(SPA(parp), &pni->ni_ip, pae->ae_prlen); bzero(THA(parp), pae->ae_hwlen); blkcopy(TPA(parp), pae->ae_pra, pae->ae_prlen); arplen = ARP_HLEN + 2*(parp->ar_hwlen + parp->ar_prlen); write(pni->ni_dev, pep, EP_HLEN+arplen); //發送請求分組 return OK; }
輸入模塊
從一個隊列中拿走一個分組,並連同解析出的物理地址一起發送給數據報鏈路層傳輸。
主要步驟為
1. 睡眠,直到ARP分組到達(請求或回答)。
2. 檢查高速緩存表,尋找對應這個ARP分組的項目。
3. if ( 找到 ){
if ( 狀態是 RESOLVED ){
更新項目;
return;
}
else if ( 狀態是 PENDING ){
更新項目;
如果隊列非空的話,將一個分組從隊列中取出,將它與硬件地址一起發送給數據鏈路層;
return;
{
}
else{
創建一個項目;
將此項目添加到表中;
return;
}
4. 如果分組是一個請求, 發送ARP回答。
向表中增加已轉換的表項
/* arpadd.c - arpadd */ #include <conf.h> #include <kernel.h> #include <network.h> struct arpentry *arpalloc(void); /*------------------------------------------------------------------------ * arpadd - Add a RESOLVED entry to the ARP cache * N.B. Assumes interrupts disabled *------------------------------------------------------------------------ */ struct arpentry * arpadd(struct netif *pni, struct arp *parp) { struct arpentry *pae; pae = arpalloc(); //在高速緩存中分配一個表項 /* 利用ARP分組信息填寫表項 */ pae->ae_hwtype = parp->ar_hwtype; pae->ae_prtype = parp->ar_prtype; pae->ae_hwlen = parp->ar_hwlen; pae->ae_prlen = parp->ar_prlen; pae->ae_pni = pni; pae->ae_queue = EMPTY; memcpy(pae->ae_hwa, SHA(parp), parp->ar_hwlen); memcpy(pae->ae_pra, SPA(parp), parp->ar_prlen); /* 初始化 */ pae->ae_ttl = ARP_TIMEOUT; pae->ae_state = AS_RESOLVED; return pae; }
發送等待發送的分組
/* arpqsend.c - arpqsend */ #include <conf.h> #include <kernel.h> #include <network.h> int netwrite(struct netif *, struct ep *, unsigned); /*------------------------------------------------------------------------ * arpqsend - write packets queued waiting for an ARP resolution *------------------------------------------------------------------------ */ void arpqsend(struct arpentry *pae) { struct ep *pep; struct netif *pni; if (pae->ae_queue == EMPTY) return; pni = pae->ae_pni; /* 遍歷等待發送的分組隊列,調用netwrite逐個放入網絡輸出隊列中 */ while (pep = (struct ep *)deq(pae->ae_queue)) netwrite(pni, pep, pep->ep_len); freeq(pae->ae_queue); //隊列為空后,釋放自身 pae->ae_queue = EMPTY; }
高速緩存管理
高速緩存是用來存儲IP地址與物理地址的。如果一個IP進行需要發送一個數據報,但其目的地址不在ARP高速緩存中,就會創建一個新的表項,然后廣播相應的請求分組,並等待分組置入隊列中。
主要步驟:
1. 睡眠,周期性的喚醒。
2. 遍歷高速緩存的每一個項目:
if ( 狀態為FREE )
continue;
if ( 狀態為PENDING ){
嘗試次數+1;
if ( 嘗試次數達到最大次數 ){
該項目狀態->FREE;
撤銷相應的隊列;
}
else {
發送ARP請求;
}
continue;
}
else if( 狀態為RESOLVED ){
將超時字段的值減去已經過去的時間;
若結果小於0,狀態->FREE,撤銷相應隊列。
}
替換策略
/* arpalloc.c - arpalloc */ #include <conf.h> #include <kernel.h> #include <proc.h> #include <network.h> void arpdq(struct arpentry *); /*------------------------------------------------------------------------ * arpalloc - allocate an entry in the ARP table * N.B. Assumes interrupts DISABLED *------------------------------------------------------------------------ */ struct arpentry *arpalloc() { static int aenext = 0; //靜態變量,保證循環 struct arpentry *pae; int i; for (i=0; i<ARP_TSIZE; ++i) { //遍歷表 if (arptable[aenext].ae_state == AS_FREE) break; aenext = (aenext + 1) % ARP_TSIZE; //循環替換 } pae = & arptable[aenext]; aenext = (aenext + 1) % ARP_TSIZE; if (pae->ae_state == AS_PENDING && pae->ae_queue >= 0) arpdq(pae); pae->ae_state = AS_PENDING; return pae; }