博主前言
本篇博客是對Kaggle上的基於機器學習對惡意代碼檢測的源碼的設計思路進行解析,裏面的解析,是提煉寫代碼人解決問題的方案與精髓,僅供學習,此外,裏面的解析難免有不當的之處,敬請讀者斧正
摘要
要想檢測惡意代碼,首先我們必須對其進行特徵提取,但是惡意代碼的特徵向量的維度是非常高的,在計算機中處理起來,計算速度會非常的慢,但是也不必擔心,能夠檢測出惡意代碼是否惡意,影響較大的僅僅是幾個特徵,爲此,在進行分類學習之前我們需要對其進行數據預處理,編寫這一段代碼的作者,採用預先剔除無關影響的特徵向量,留下影響較大的特徵向量!已達到分類準確的目的,本篇博客是基於機器學習惡意代碼檢測之數據預處理的第四種處理方法
案例分析(4)
## 常規套路:
# 第1步:根據需要導入必要的第三方庫**
import numpy as np
import pandas as pd
from tqdm import tqdm
import gc
import lightgbm as lgb
from sklearn.model_selection import KFold
import warnings
import gc
import time
import sys
import datetime
import os
print(os.listdir("../input"))
# 第2步:預先知道文件的內容,羅列出所有特徵,瞭解其所對應的特徵取值的數據類型
dtypes =
{
'MachineIdentifier': 'category',
'ProductName': 'category',
'EngineVersion': 'category',
'AppVersion': 'category',
'AvSigVersion': 'category',
'IsBeta': 'int8',
'RtpStateBitfield': 'float16',
'IsSxsPassiveMode': 'int8',
'DefaultBrowsersIdentifier': 'float16',
'AVProductStatesIdentifier': 'float32',
'AVProductsInstalled': 'float16',
'AVProductsEnabled': 'float16',
'HasTpm': 'int8',
'CountryIdentifier': 'int16',
'CityIdentifier': 'float32',
'OrganizationIdentifier': 'float16',
'GeoNameIdentifier': 'float16',
'LocaleEnglishNameIdentifier': 'int8',
'Platform': 'category',
'Processor': 'category',
'OsVer': 'category',
'OsBuild': 'int16',
'OsSuite': 'int16',
'OsPlatformSubRelease': 'category',
'OsBuildLab': 'category',
'SkuEdition': 'category',
'IsProtected': 'float16',
'AutoSampleOptIn': 'int8',
'PuaMode': 'category',
'SMode': 'float16',
'IeVerIdentifier': 'float16',
'SmartScreen': 'category',
'Firewall': 'float16',
'UacLuaenable': 'float32',
'Census_MDC2FormFactor': 'category',
'Census_DeviceFamily': 'category',
'Census_OEMNameIdentifier': 'float16',
'Census_OEMModelIdentifier': 'float32',
'Census_ProcessorCoreCount': 'float16',
'Census_ProcessorManufacturerIdentifier': 'float16',
'Census_ProcessorModelIdentifier': 'float16',
'Census_ProcessorClass': 'category',
'Census_PrimaryDiskTotalCapacity': 'float32',
'Census_PrimaryDiskTypeName': 'category',
'Census_SystemVolumeTotalCapacity': 'float32',
'Census_HasOpticalDiskDrive': 'int8',
'Census_TotalPhysicalRAM': 'float32',
'Census_ChassisTypeName': 'category',
'Census_InternalPrimaryDiagonalDisplaySizeInInches': 'float16',
'Census_InternalPrimaryDisplayResolutionHorizontal': 'float16',
'Census_InternalPrimaryDisplayResolutionVertical': 'float16',
'Census_PowerPlatformRoleName': 'category',
'Census_InternalBatteryType': 'category',
'Census_InternalBatteryNumberOfCharges': 'float32',
'Census_OSVersion': 'category',
'Census_OSArchitecture': 'category',
'Census_OSBranch': 'category',
'Census_OSBuildNumber': 'int16',
'Census_OSBuildRevision': 'int32',
'Census_OSEdition': 'category',
'Census_OSSkuName': 'category',
'Census_OSInstallTypeName': 'category',
'Census_OSInstallLanguageIdentifier': 'float16',
'Census_OSUILocaleIdentifier': 'int16',
'Census_OSWUAutoUpdateOptionsName': 'category',
'Census_IsPortableOperatingSystem': 'int8',
'Census_GenuineStateName': 'category',
'Census_ActivationChannel': 'category',
'Census_IsFlightingInternal': 'float16',
'Census_IsFlightsDisabled': 'float16',
'Census_FlightRing': 'category',
'Census_ThresholdOptIn': 'float16',
'Census_FirmwareManufacturerIdentifier': 'float16',
'Census_FirmwareVersionIdentifier': 'float32',
'Census_IsSecureBootEnabled': 'int8',
'Census_IsWIMBootEnabled': 'float16',
'Census_IsVirtualDevice': 'float16',
'Census_IsTouchEnabled': 'int8',
'Census_IsPenCapable': 'int8',
'Census_IsAlwaysOnAlwaysConnectedCapable': 'float16',
'Wdft_IsGamer': 'float16',
'Wdft_RegionIdentifier': 'float16',
'HasDetections': 'int8'
}
# 第3步:將特徵所對應的的特徵可能取值的數據類型歸類
numerics = ['int8', 'int16', 'int32', 'int64', 'float16', 'float32', 'float64']
# 數據類型爲數值的特徵列
numerical_columns = [c for c,v in dtypes.items() if v in numerics]
# 數據類型爲“字符型”的特徵列
categorical_columns = [c for c,v in dtypes.items() if v not in numerics]
# 第4步:讀取文件
retained_columns = numerical_columns + categorical_columns
train = pd.read_csv('../input/train.csv',usecols = retained_columns,dtype = dtypes)
# 第5步:將數據類型進一步細分
# 真正意義上數據類型是數值的特徵列
true_numerical_columns =
[
'Census_ProcessorCoreCount',
'Census_PrimaryDiskTotalCapacity',
'Census_SystemVolumeTotalCapacity',
'Census_TotalPhysicalRAM',
'Census_InternalPrimaryDiagonalDisplaySizeInInches',
'Census_InternalPrimaryDisplayResolutionHorizontal',
'Census_InternalPrimaryDisplayResolutionVertical',
'Census_InternalBatteryNumberOfCharges'
]
# 特徵可能取值爲2的特徵列(裏面有數值類型和“字符串類型”)
binary_variables = [c for c in train.columns if train[c].nunique() == 2]
# 數據類型爲“字符串型”的特徵列
categorical_columns = [c for c in train.columns if (c not in true_numerical_columns) & (c not in binary_variables)]
# 統計一下不同數據類型的特徵出現個數,並整理成集合的形式
variables =
{
'categorical_columns': len(categorical_columns),
'binary_variables': len(binary_variables),
'true_numerical_columns': len(true_numerical_columns)
}
上述的準備工作完成以後,我們進入正題:特徵工程
爲了減小算力,我們可以將“字符型”的特徵變量的取值進行編碼,但是編碼的原則是吧可能取值種數較多的
# 創建一個空的列表,這裏面的元素是對應的“字符型”的特徵變量以及它們的可能取值數的一個子列表
cardinality = []
# 通過遍歷將所有的“字符型”的特徵變量可能取值數放入上述的空列表中
for c in categorical_columns:
if c == 'MachineIdentifier': continue
cardinality.append([c, train[c].nunique()])
# 根據種數的排序選取種數較多的幾個特徵列
cardinality.sort(key = lambda x:x[1], reverse=False)
## 定義一個編碼前的數據預處理的函數
def frequency_encoding(variable):
t = train[variable].value_counts().reset_index()
# 再調用一次reset_index()會自動生成level_0列
t = t.reset_index()
# 找出當前特徵可能取值僅有1,level_0列用缺失值nan來代替,一種取值不易分類
t.loc[t[variable] == 1, 'level_0'] = np.nan
# 將index列的元素設置爲索引
t.set_index('index', inplace=True)
# 通過這樣的方法可以找到
max_label = t['level_0'].max() + 1
t.fillna(max_label, inplace=True)
# 用to_list()函數將索引作爲鍵,level_0所對應的值作爲值組成字典
return t.to_dict()['level_0']
## 知識補充:
# set_index():將某一列的值設置爲索引
# reset_index():將某一列的索引重新恢復爲值
#
indexer = {}
for col in tqdm(categorical_columns):
if col == 'MachineIdentifier': continue
# 對應的特徵變量爲鍵,factorize解包後第二維的值爲一一對應的編碼
_, indexer[col] = pd.factorize(train[col])
## 知識補充:
## pandas中factorize函數是返回一個元組的函數,第一維的值是編碼的結果,第二維的值是要編碼的索引
# 將所有特徵列按照裝載好的特徵索引字典進行設定標籤
for col in tqdm(categorical_columns):
if col == 'MachineIdentifier': continue
train[col] = indexer[col].get_indexer(train[col])
## 構建循環將調用上面frequency_encoding(variable)函數(規定的編碼規則進行編碼)
freq_enc_dict_dict = {}
for variable in tqdm(['Census_OEMModelIdentifier', 'CityIdentifier', 'Census_FirmwareVersionIdentifier']):
freq_enc_dict_dict[variable] = frequency_encoding(variable)
train[variable] = train[variable].map(lambda x: freq_enc_dict_dict[variable].get(x, np.nan))
verbose=True
# 這裏需要進行數據集內存的調整
numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
start_mem = train.memory_usage().sum() / 1024**2
# 每一個數據類型爲數值的特徵列都遍歷一遍去尋找,找到其數據類型所佔的內存在那一部分合適的內存範圍,這樣避免造成可以節省內存空間造成不必要的空間浪費
for col in tqdm(train.columns):
col_type = train[col].dtypes
if col_type in numerics:
# 在每一個特徵列中找到佔據內存最大,最小的數據類型
c_min = train[col].min()
c_max = train[col].max()
# 如果前三個數據的數據類型爲整形,那麼這一組數據存儲方式就是以整形的放手進行存儲,接下來就是考慮是8位整型還是其他位數的整型
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
train[col] = train[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
train[col] = train[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
train[col] = train[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
train[col] = train[col].astype(np.int64)
# 否則該列的數據存儲方式以浮點形式進行存儲
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
train[col] = train[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
train[col] = train[col].astype(np.float32)
else:
train[col] = train[col].astype(np.float64)
## 知識補充:pandas第三方庫中iinfo函數是一個計算一個series數據結構中每一個數值所佔的內存
end_mem = train.memory_usage().sum() / 1024**2
if verbose:
print('Mem. usage decreased to {:5.2f} Mb ({:.1f}% reduction)'.format(end_mem, 100 * (start_mem - end_mem) / start_mem))