博主前言
本篇博客是对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))