動漫制作專業(yè)簡介桂林網(wǎng)站優(yōu)化
阿里云安全惡意程序檢測
- 賽題理解
- 賽題介紹
- 賽題說明
- 數(shù)據(jù)說明
- 評測指標
- 賽題分析
- 數(shù)據(jù)特征
- 解題思路
- 數(shù)據(jù)探索
- 數(shù)據(jù)特征類型
- 數(shù)據(jù)分布
- 箱型圖
- 變量取值分布
- 缺失值
- 異常值
- 分析訓練集的tid特征
- 標簽分布
- 測試集數(shù)據(jù)探索同上
- 數(shù)據(jù)集聯(lián)合分析
- file_id分析
- API分析
- 特征工程與基線模型
- 構(gòu)造特征與特征選擇
- 基于數(shù)據(jù)類型的方法
- 基于多分析視角的方法
- 特征選擇
- 構(gòu)造線下驗證集
- 評估穿越
- 訓練集和測試集的特征性差異
- 訓練集和測試集是分布差異性
- 基線模型
- 特征工程
- 基線構(gòu)建
- 特征重要性分析
- 模型測試
賽題理解
賽題介紹
賽題說明
本題目提供的數(shù)據(jù)來自經(jīng)過沙箱程序模擬運行后的API指令序列,全為Windows二進制可執(zhí)行程序,經(jīng)過脫敏處理:樣本數(shù)據(jù)均來自互聯(lián)網(wǎng),其中惡意文件的類型有感染型病毒、木馬程序、挖礦程序、DDoS 木馬、勒索病毒等,數(shù)據(jù)總計6億條。
注:什么是沙箱程序?
在計算機安全中,沙箱(Sandbox)是一種用于隔離正在運行程序的安全機制,通常用于執(zhí)行未經(jīng)測試或者不受信任的程序或代碼,它會為待執(zhí)行的程序創(chuàng)建一個獨立的執(zhí)行環(huán)境,內(nèi)部程序的執(zhí)行不會影響到外部程序的運行。
數(shù)據(jù)說明
評測指標
需特別注意,log 對于小于1的數(shù)是非常敏感的。比如log0.1和log0.000 001的單個樣本的誤差為10左右,而log0.99和log0.95的單個誤差為0.1左右。
logloss和AUC的區(qū)別:AUC只在乎把正樣本排到前面的能力,logloss更加注重評估的準確性。如果給預測值乘以一個倍數(shù),則AUC不會變,但是logloss 會變。
賽題分析
數(shù)據(jù)特征
本賽題的特征主要是API接口的名稱,這是融合時序與文本的數(shù)據(jù),同時接口名稱基本表達了接口用途。因此,最基本、最簡單的特征思路是對所有API數(shù)據(jù)構(gòu)造CountVectorizer特征。
說明: CountVectorizer 是屬于常見的特征數(shù)值計算類,是一個文本特征提取方法。對于每一個訓練文本,它只考慮每種詞匯在該訓練文本中出現(xiàn)的頻率。
解題思路
本賽題根據(jù)官方提供的每個文件對API的調(diào)用順序及線程的相關(guān)信息按文件進行分類,將文件屬于每個類的概率作為最終的結(jié)果進行提交,并采用官方的logloss作為最終評分,屬于典型的多分類問題。
數(shù)據(jù)探索
數(shù)據(jù)特征類型
train.info()
train.head(5)
train.describe()
數(shù)據(jù)分布
箱型圖
#使用箱型圖查看單個變量的分布情況。
#取前10000條數(shù)據(jù)繪制tid變量的箱型圖
#os:當數(shù)據(jù)量太大時,變量可視化取一部分
sns.boxplot(x = train.iloc[:10000]["tid"])
變量取值分布
#用函數(shù)查看訓練集中變量取值的分布
train.nunique()
缺失值
#查看缺失值
train.isnull().sum()
異常值
#異常值:分析訓練集的index特征
train['index'].describe()
分析訓練集的tid特征
#分析訓練集的tid特征
train['tid'].describe()
標簽分布
#統(tǒng)計標簽取值的分布情況
train['label'].value_counts()
直觀化:
train['label'].value_counts().sort_index().plot(kind = 'bar')
train['label'].value_counts().sort_index().plot(kind = 'pie')
測試集數(shù)據(jù)探索同上
數(shù)據(jù)集聯(lián)合分析
file_id分析
#對比分析file_id變量在訓練集和測試集中分布的重合情況:
train_fileids = train['file_id'].unique()
test_fileids = test['file_id'].unique()
len(set(train_fileids) - set(test_fileids))
API分析
#對比分析API變量在訓練集和測試集中分布的重合情況
train_apis = train['api'].unique()
test_apis = test['api'].unique()
set(set(test_apis) - set(train_apis))
特征工程與基線模型
構(gòu)造特征與特征選擇
基于數(shù)據(jù)類型的方法
基于多分析視角的方法
這是最常見的一種特征構(gòu)造方法,在所有的基于table 型(結(jié)構(gòu)化數(shù)據(jù))的比賽中都會用到。
我們以用戶是否會在未來三天購買同一物品為例,來說明此類數(shù)據(jù)的構(gòu)建角度:用戶長期購物特征,用戶長期購物頻率;用戶短期購物特征,用戶近期購物頻率;物品受歡迎程度,該物品最近受歡迎程度;
用戶對此類產(chǎn)品的喜好特征:用戶之前購買該類/該商品的頻率等信息;
時間特征:是否到用戶發(fā)工資的時間段:商品是否為用戶的必備品,如洗漱用品、每隔多長時間必買等。
特征選擇
特征選擇主要包含過濾法、包裝法和嵌入法三種,前面已經(jīng)介紹過。
構(gòu)造線下驗證集
在數(shù)據(jù)競賽中,為了防止選手過度刷分和作弊,每日的線上提交往往是有次數(shù)限制的。因此,線下驗證集的構(gòu)造成為檢驗特征工程、模型是否有效的關(guān)鍵。在構(gòu)造線下驗證集時,我們需要考慮以下幾個方面的問題。
評估穿越
評估穿越最常見的形式是時間穿越和會話穿越兩種。
1.時間穿越
例1: 假設(shè)我們需要預測用戶是否會去觀看視頻B,在測試集中需要預測用戶8月8日上午10:10點擊觀看視頻B的概率,但是在訓練集中已經(jīng)發(fā)現(xiàn)該用戶8月8日上午10:09在觀看視頻A,上午10:11 也在觀看視頻A,那么很明顯該用戶就有非常大的概率不看視頻B,通過未來的信息很容易就得出了該判斷。
例2: 假設(shè)我們需要預測用戶9月10日銀行卡的消費金額,但是在訓練集中已經(jīng)出現(xiàn)了該用戶銀行卡的余額在9月9日和9月11日都為0,那么我們就很容易知道該用戶在9月10日的消費金額是0,出現(xiàn)了時間穿越的消息。
2.會話穿越
以電商網(wǎng)站的推薦為例,當用戶在瀏覽某一個商品時,某個推薦模塊會為他推薦多個商品進行展現(xiàn),用戶可能會點擊其中的一個或幾個。為了描述方便,我們將這些一 次展現(xiàn)中產(chǎn)生的,點擊和未點擊的數(shù)據(jù)合起來稱為一 次會話(不同于計算機網(wǎng)絡(luò)中會話的概念)。在上面描述的樣本劃分方法中,一次會話中的樣本可能有一部分被劃分到訓練集,另一部分被劃分到測試集。這樣的行為,我們稱之為會話穿越。
會話穿越的問題在于,由于一個會話對應(yīng)的是
一個用戶在一次展現(xiàn)中的行為,因此存在較高的相關(guān)性,穿越會帶來類似上面提到的用練習題考試的問題。此外,會話本身是不可分割的,也就是說,在線上使用模型時,不可能讓你先看到一次會話的一部分,然后讓你預測剩余的部分,因為會話的展現(xiàn)結(jié)果是一次性產(chǎn)生的,一旦產(chǎn)生后,模型就已經(jīng)無法干預展現(xiàn)的結(jié)果了。
3.穿越本質(zhì)
穿越本質(zhì)上是信息泄露的問題。無論時間穿越,還是會話穿越,其核心問題都是訓練數(shù)據(jù)中的信息以不同方式、不同程度泄露到了測試數(shù)據(jù)中。.
訓練集和測試集的特征性差異
我們用訓練集訓練模型,當訓練集和測試集的特征分布有差異時,就容易造成模型偏差,導致預測不準確。常見的訓練集和測試集的特征差異如下:
數(shù)值特征:訓練集和測試集的特征分布交叉部分極小;
類別特征:測試集中的特征大量未出現(xiàn)在訓練集中。例如,在微軟的一場比賽中,測試集中的很多版本未出現(xiàn)在訓練集中。
在某些極端情況下,訓練集中極強的特征會在測試集中全部缺失。
訓練集和測試集是分布差異性
訓練集和測試集的分布差異性的判斷步驟如下:
將訓練集的數(shù)據(jù)標記為label=1,將測試集的數(shù)據(jù)標記為label= 0。對訓練集和測試集做5折的auc交叉驗證。如果auc在0.5附近,那么則說明訓練集和測試集的分布差異不大:如果auc在0.9附近,那么則說明訓練集和測試集的分布差異很大。
基線模型
導包 -> 讀取數(shù)據(jù) -> 特征工程
特征工程
·利用count()函數(shù)和nunique()函數(shù)生成特征:反應(yīng)樣本調(diào)用api,tid,index的頻率信息
def simple_sts_features(df):simple_fea = pd.DataFrame()simple_fea['file_id'] = df['file_id'].unique()simple_fea = simple_fea.sort_values('file_id')df_grp = df.groupby('file_id')simple_fea['file_id_api_count'] = df_grp['api'].count().valuessimple_fea['file_id_api_nunique'] = df_grp['api'].nunique().valuessimple_fea['file_id_tid_count'] = df_grp['tid'].count().valuessimple_fea['file_id_tid_nunique'] = df_grp['tid'].nunique().valuessimple_fea['file_id_index_count'] = df_grp['index'].count().valuessimple_fea['file_id_index_nunique'] = df_grp['index'].nunique().valuesreturn simple_fea
·利用main(),min(),std(),max()函數(shù)生成特征:tid,index可認為是數(shù)值特征,可提取對應(yīng)的統(tǒng)計特征。
def simple_numerical_sts_features(df):simple_numerical_fea = pd.DataFrame()simple_numerical_fea['file_id'] = df['file_id'].unique()simple_numerical_fea = simple_numerical_fea.sort_values('file_id')df_grp = df.groupby('file_id')simple_numerical_fea['file_id_tid_mean'] = df_grp['tid'].mean().valuessimple_numerical_fea['file_id_tid_min'] = df_grp['tid'].min().valuessimple_numerical_fea['file_id_tid_std'] = df_grp['tid'].std().valuessimple_numerical_fea['file_id_tid_max'] = df_grp['tid'].max().valuessimple_numerical_fea['file_id_index_mean'] = df_grp['index'].mean().valuessimple_numerical_fea['file_id_index_min'] = df_grp['index'].min().valuessimple_numerical_fea['file_id_index_std'] = df_grp['index'].std().valuessimple_numerical_fea['file_id_index_max'] = df_grp['index'].max().valuesreturn simple_numerical_fea
·利用定義的特征生成函數(shù),并生成訓練集和測試集的統(tǒng)計特征。
%%time
#統(tǒng)計api,tid,index的頻率信息的特征統(tǒng)計
simple_train_fea1 = simple_sts_features(train)
%%time
simple_test_fea1 = simple_sts_features(test)
%%time
#統(tǒng)計tid,index等數(shù)值特征的特征統(tǒng)計
simple_train_fea2 = simple_numerical_sts_features(train)
%%time
simple_test_fea2 = simple_numerical_sts_features(test)
基線構(gòu)建
獲取標簽:
#獲取標簽
train_label = train[['file_id','label']].drop_duplicates(subset=['file_id','label'],keep='first')
test_submit = test[['file_id']].drop_duplicates(subset=['file_id'],keep='first')
訓練集和測試集的構(gòu)建:
#訓練集和測試集的構(gòu)建
train_data = train_label.merge(simple_train_fea1,on = 'file_id',how = 'left')
train_data = train_data.merge(simple_train_fea2,on = 'file_id',how = 'left')test_submit = test_submit.merge(simple_test_fea1,on = 'file_id',how = 'left')
test_submit = test_submit.merge(simple_test_fea2,on = 'file_id',how = 'left')
因為本賽題給出的指標和傳統(tǒng)的指標略有不同,所以需要自己寫評估指標,這樣方便對比線下與線上的差距,以判斷是否過擬合、是否出現(xiàn)線上線下不一致的問題等。
#關(guān)于LGB的自定義評估指標的書寫
def lgb_logloss(preds,data):labels_ = data.get_label()classes_ = np.unique(labels_)preds_prob = []for i in range(len(classes_)):preds_prob.append(preds[i * len(labels_):(i+1)*len(labels_)])preds_prob_ = np.vstack(preds_prob)loss = []for i in range(preds_prob_.shape[1]): #樣本個數(shù)sum_ = 0for j in range(preds_prob_.shape[0]): #類別個數(shù)pred = preds_prob_[j,i] #第i個樣本預測為第j類的概率if j == labels_[i]:sum_ += np.log(pred)else:sum_ += np.log(1 - pred)loss.append(sum_)return 'loss is: ',-1 * (np.sum(loss) / preds_prob_.shape[1]),False
線下驗證:
train_features = [col for col in train_data.columns if col not in ['label','file_id']]
train_label = 'label'
使用5折交叉驗證,采用LGB模型:
%%timefrom sklearn.model_selection import StratifiedKFold,KFold
params = {'task':'train','num_leaves':255,'objective':'multiclass','num_class':8,'min_data_in_leaf':50,'learning_rate':0.05,'feature_fraction':0.85,'bagging_fraction':0.85,'bagging_freq':5,'max_bin':128,'random_state':100
}folds = KFold(n_splits=5,shuffle=True,random_state = 15) #n_splits = 5定義5折
oof = np.zeros(len(train))predict_res = 0
models = []
for fold_, (trn_idx,val_idx) in enumerate(folds.split(train_data)):print("fold n°{}".format(fold_))trn_data = lgb.Dataset(train_data.iloc[trn_idx][train_features],label = train_data.iloc[trn_idx][train_label].values)val_data = lgb.Dataset(train_data.iloc[val_idx][train_features],label = train_data.iloc[val_idx][train_label].values)clf = lgb.train(params,trn_data,num_boost_round = 2000,valid_sets = [trn_data,val_data],verbose_eval = 50,early_stopping_rounds = 100,feval = lgb_logloss)models.append(clf)
特征重要性分析
#特征重要性分析
feature_importance = pd.DataFrame()
feature_importance['fea_name'] = train_features
feature_importance['fea_imp'] = clf.feature_importance()
feature_importance = feature_importance.sort_values('fea_imp',ascending = False)
plt.figure(figsize = [20,10,])
sns.barplot(x = feature_importance['fea_name'],y = feature_importance['fea_imp'])
由運行結(jié)果可以看出:
(1) API的調(diào)用次數(shù)和API的調(diào)用類別數(shù)是最重要的兩個特征,即不同的病毒常常會調(diào)用不同的API,而且由于有些病毒需要復制自身的原因,因此調(diào)用API的次數(shù)會明顯比其他不同類別的病毒多。
(2)第三到第五強的都是線程統(tǒng)計特征,這也較為容易理解,因為木馬等病毒經(jīng)常需要通過線程監(jiān)聽一些內(nèi)容,所以在線程等使用上會表現(xiàn)的略有不同。
模型測試
#模型測試
pred_res = 0
fold = 5
for model in models:pred_res += model.predict(test_submit[train_features]) * 1.0 /fold
test_submit['prob0'] = 0
test_submit['prob1'] = 0
...
test_submit[['prob0','prob1','prob2','prob3','prob4','prob5','prob6','prob7']] = pred_res
test_submit[['file_id','prob0','prob1','prob2','prob3','prob4','prob5','prob6','prob7']].to_csv('baseline.csv',index = None)