如果将AI工程分为两块, 数据处理 模型相关 那么,特征工程就是处理数据的,将业务数据转化为模型需要的数据
图片类数据看看图片什么样,以便后继标注;这里说的特征工程主要指二维数表,图像处理请移驾CV专栏 二维数据表,则观察不同列的数据分布: 空值处理 编码:字符串转数字 将数据全部转为数字后,后面就观察其分布: 均值,方差,min,max,25%,50%,75%,90%,99%附近的数据 离群点,异常处理数据 如果列比较多,则会分析一下列之间的线性关系,是否有严重重复的,也就是特征选择: 卡方 IV值 共线性 看完数据什么样后,对数据的规模,行数,列数,列的含义等数据特点有所了解之后, 确认业务是分类问题还是回归问题, 标签是什么 以便考虑选择什么样的模型
|
import datetime
import os
from typing import Callable, Optional
import pandas as pd
from sklearn import preprocessing
import numpy as np
import torch
from torch_geometric.data import (
Data,
InMemoryDataset
)
pd.set_option('display.max_columns', None)
data_path = "/wks/datasets/ibm_aml/HI-Small_Trans.csv"
data=pd.read_csv(data_path)
df = data
df[:3]
数字类型
df[['Amount Received', 'Amount Paid']].describe()
数字看大小分布
类别看频次分布
df[['Account', 'Account.1']].describe()
如果列是非数值类型(如字符串或分类类型), describe() 默认只会显示 count、unique(唯一值数量)、 top(出现频率最高的值)和 freq(top 值的出现次数)。
|
df['To Bank'].value_counts()
|
df = raw_df.sample(n=50000, random_state=1) df.shape |
|
|
|
|
df.isnull().sum()
----------------------------------------------------------------------
|
import datetime
import os
import pandas as pd
from sklearn import preprocessing
import numpy as np
import torch
pd.set_option('display.max_columns', None)
data_path = "/wks/datasets/ibm_aml/HI-Small_Trans.csv"
data=pd.read_csv(data_path)
df = data
df[:3]
## Dropping some columns cols_to_drop = ['Timestamp', 'Amount Paid', 'Payment Currency'] df.drop(cols_to_drop, axis=1, inplace=True) df.head(2) |
|
|
|
|
|
|
import datetime
import os
from typing import Callable, Optional
import pandas as pd
from sklearn import preprocessing
import numpy as np
import torch
from torch_geometric.data import (
Data,
InMemoryDataset
)
pd.set_option('display.max_columns', None)
data_path = "/wks/datasets/ibm_aml/HI-Small_Trans.csv"
data=pd.read_csv(data_path)
df = data
df[:3]
df.info()
class 'pandas.core.frame.DataFrame'
Index: 50000 entries, 4205263 to 1446591
Data columns (total 11 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Timestamp 50000 non-null object
1 From Bank 50000 non-null int64
2 Account 50000 non-null object
3 To Bank 50000 non-null int64
4 Account.1 50000 non-null object
5 Amount Received 50000 non-null float64
6 Receiving Currency 50000 non-null object
7 Amount Paid 50000 non-null float64
8 Payment Currency 50000 non-null object
9 Payment Format 50000 non-null object
10 Is Laundering 50000 non-null int64
dtypes: float64(2), int64(3), object(6)
memory usage: 4.6+ MB
numeric_cols = df.select_dtypes(exclude="object").columns
numeric_cols
Index(['From Bank', 'To Bank', 'Amount Received', 'Amount Paid',
'Is Laundering'],
dtype='object')
categorical_cols = df.select_dtypes(include="object").columns
categorical_cols
Index(['Timestamp', 'Account', 'Account.1', 'Receiving Currency',
'Payment Currency', 'Payment Format'],
dtype='object')
参考代码 /opt/wks/kejian/zhunbei/dl_kejian/gnn/ibm-aml |
## Unique columns in df
unique_counts = df[categorical_cols].nunique()
print("Unique columns in the DataFrame: \n", unique_counts)
Unique columns in the DataFrame: Timestamp 13001 Account 37434 Account.1 44321 Receiving Currency 15 Payment Currency 15 Payment Format 7 dtype: int64 |
|
LabelEncoder
def df_label_encoder(df, columns):
le = preprocessing.LabelEncoder()
for i in columns:
df[i] = le.fit_transform(df[i].astype(str))
return df
df = df_label_encoder(df,['Payment Format', 'Payment Currency', 'Receiving Currency'])
保存类别列表
from sklearn import preprocessing
from tpf import pkl_save,pkl_load
label_encoding_dict_path = "label_encoding_dict.pkl"
def df_label_encoder(df, columns,file_path):
if os.path.exists(file_path):
label_encoding_dict = pkl_load(file_path)
for i in columns:
le = preprocessing.LabelEncoder()
le.classes_ = label_encoding_dict[i]
df[i] = le.transform(df[i].astype(str))
else:
le = preprocessing.LabelEncoder()
label_encoding_dict = {}
for i in columns:
df[i] = le.fit_transform(df[i].astype(str))
label_encoding_dict[i] = le.classes_
pkl_save(label_encoding_dict,file_path=file_path)
return df
df = df_label_encoder(df,['Payment Format', 'Receiving Currency'],label_encoding_dict_path)
df[:3]
|
|
多列共用指定字典,有新类型时自动扩展字典(索引0为未知类型) #IMPORT STATEMENTS import numpy as np import pandas as pd import os BASE_DIR = "/wks/datasets/ibm_aml" data_path = os.path.join(BASE_DIR,"HI-Small_Trans.csv") df=pd.read_csv(data_path) data_test = df.head(2) data_test
from tpf.d1 import CsvStat st = CsvStat(data=data_test,print_level=2,) cls_dict = st.word2id(["From Bank","Account",'Receiving Currency','Payment Currency', 'Payment Format']) st.head(3)
银行与账户虽然都是两列,但应该使用同一套索引编码 cls_dict2 = st.word2id(["Account.1"],word2id=cls_dict["Account"]) cls_dict["Account"] = cls_dict2['Account.1'] cls_dict2 = st.word2id(["To Bank"],word2id=cls_dict["From Bank"]) cls_dict["From Bank"] = cls_dict2['To Bank'] st.head(3)
UNK':0,即每一列的索引0代表未记录的词 |
import pandas as pd
from tpf.link import FeatureEngine
data = {
"c1":["A","B","C"],
"c2":["A","D","E"]
}
df = pd.DataFrame(data)
df
fe = FeatureEngine()
cls_dict = fe.cls2index(df, classify_type=["c1","c2"],word2id={})
cls_dict
{'UNK': 0,
'c1': {'D': 4, 'E': 5, 'A': 1, 'C': 2, 'B': 3},
'c2': {'D': 4, 'E': 5, 'A': 1, 'C': 2, 'B': 3}}
|
import pandas as pd
data = {
"c1":["A","B","C"],
"c2":["A","D","E"]
}
df = pd.DataFrame(data)
print(df)
c1 c2
0 A A
1 B D
2 C E
from tpf.nlp.text import TextEmbedding as tte
tte.col2index(df,classify_type=['c1','c2'],
classify_type2=[],
dict_file="a.dict",
is_pre=False,
word2id=None,)
这是早先的错误版本:所有列做一个字典使用
print(df)
c1 c2
0 1 1
1 3 2
2 4 5
aa = {"a":1}
if "a" in aa:
print(aa["a"])
修正后版本:列与列相互独立,如果需要合并则指定classify_type2
import pandas as pd
data = {
'a1':["a","b","c"],
"c1":["A","B","C"],
"c2":["A","D","E"]
}
df = pd.DataFrame(data)
print(df)
a1 c1 c2
0 a A A
1 b B D
2 c C E
from tpf.nlp.text import TextEmbedding as tte
tte.col2index(df,classify_type=['a1'],
classify_type2=[['c1','c2']],
dict_file="a.dict",
is_pre=False,
word2id=None,)
df
a1 c1 c2
0 3 1 1
1 2 4 3
2 1 2 0
---------------------------------------------------------------------
|
将全体数据集的整个尺度 映射到 [0,1]的范围
或者是[-1,1]这种以0为中心,绝对值在1以内的范围内
最大最小标准化,离散标准化,将数据映射到0-1之间 X = (X - min)/(max - min) 如果最大值 与 最小值中出现异常值,对结果会产生较大的影响 适合 波动不大 的小量数据处理 由于是两个极值想减,有的地方也叫 极差化 也叫线性归一化 折中处理 为了防止min,max出现异常对数据处理造成较大影响 采用99%分位数作为new_max,超出的部分抹去,最大为new_max 采用1%分位数作为new_min ,低于的部分抹去,最小为new_min
from sklearn.preprocessing import MinMaxScaler
preprocess = MinMaxScaler()
raw_data = [[1, 1, 1],
[3, 3, 3],
[1, 2, 3]]
data = preprocess.fit_transform(raw_data)
data
array([[0. , 0. , 0. ],
[1. , 1. , 1. ],
[0. , 0.5, 1. ]])
以向量,向量组的角度看这个矩阵:
第一个向量为
[
1,
3,
1
]
默认是列向量,min=1,max=3,max - min = 2
代入公式,会得到一个
[
0,
1,
0]
的向量
|
(数据-均值)/(标准差 + 1e-6) 标准差就是方差的开平方, 是一个数据分布内部离散量的累加值, 可近似看作 整体数据离“数据中心点”的距离之和 这么一系列操作后,得到的是啥? 将数据转化为均值为0,标准差为1 的正态分布,即标准正态分布 为什么要这么做?为什么呢? 因为数据处理时,处理的多个数据分布,要对比就得有个统一的量纲, 处理方法就是,不管你原来是什么分布,数据的均值多少,离散程度怎么样, 我统一给你转成均值为1方差为1的标准正态分布 这是能够多列一起处理的 基石/起点/前提 这种方法,在数据量大时,异常点对数据处理影响不大 在统计学中,也叫z-score标准化
from sklearn.preprocessing import StandardScaler
preprocess = StandardScaler()
raw_data = [[1, 1, 1],
[3, 3, 3],
[1, 2, 3]]
data = preprocess.fit_transform(raw_data)
data
array([[-0.70710678, -1.22474487, -1.41421356],
[ 1.41421356, 1.22474487, 0.70710678],
[-0.70710678, 0. , 0.70710678]])
海量数据通常都是以正态分布的形式呈现
|
首先是算法层面决定了标签没有归一化的必要 比如, KNN,近邻思想,它取的是训练集X中离样本最近数据的索引, 直接按此索引在标签中取最近n个数 至于这几个数的值是多少,算法根本就不关心,不涉及 又比如决策树,是按某种方法去划分训练集X, 找到一些分支可以更好地分类标签,至于标签的值也不参与计算 再比如神经网络的分类问题,标签onehot编码都是0与1,形成E矩阵, 这是彻底归一了,不需要你再做归一化了 对于连续型变量, 机器学习中基本是近邻思想找到附近的值,取均值 深度学习中,则是会自动计算参数w,该放大就放大,该缩小就缩小, 会自动向标签靠近 另外,除了0与1,其他数据归一后,再返回原形式,前后未必完全相等 比如 >>> 1/3 0.3333333333333333 >>> 1/0.333333 3.0000030000030002 >>> 1/0.3333333 3.00000030000003 >>> 1/333 0.003003003003003003 >>> 1/0.0030030 333.000333000333 标签通常不需要归一化,这主要由算法决定 如果实际场景中需要归一化,也是可以的 |
统一量纲 不是非得归一 重在归于一个统一的数值,1可以,10也可,100也可 由于计算机计算[0,1]之间的数据更容易,两者结合后,归于[0,1] 但思维上不应该限定于1,认为非1不可,可根据场景调整 一些算法将该场景抽离成参数,由用户决定归于一个什么样的数值 大部分情况下,归于0.9完全可行,就是将数据再往下压10%, 这样在计算的过程中数据爆炸的风险就会再低一点 |
虽然对于模型来说,都是在计算数字, 但在输入模型的数据,是会区分哪些特征是连续型,哪此是离散型的 统一量纲只针对连续型特征 对于离散型特征,通常以编码的方式转为数字,然后再决定是否进行归一化处理 |
|
数字的可更新归一化
import pandas as pd
from tpf.link import dtl
# 示例数据
df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [10, 20, 30]})
df2 = pd.DataFrame({'A': [0, 4, 5], 'B': [5, 25, 35]})
df3 = pd.DataFrame({'A': [1.5, 2.5], 'B': [15, 25]})
# 首次执行 (计算并保存 min-max 到 joblib 文件)
processed1 = dtl.min_max_update(df1, num_scaler_file='scaler2.pkl', is_pre=False)
print("Processed df1:\n", processed1)
# 后续执行 (更新 min-max 并重新保存)
processed2 = dtl.min_max_update(df2, num_scaler_file='scaler2.pkl', is_pre=False)
print("Processed df2:\n", processed2)
# 预处理模式 (直接使用保存的 scaler)
processed3 = dtl.min_max_update(df3, num_scaler_file='scaler2.pkl', is_pre=True)
print("Processed df3 (preprocessing mode):\n", processed3)
Processed df1:
A B
0 0.0 0.0
1 0.5 0.5
2 1.0 1.0
Processed df2:
A B
0 0.0 0.000000
1 0.8 0.666667
2 1.0 1.000000
Processed df3 (preprocessing mode):
A B
0 0.3 0.333333
1 0.5 0.666667
深度学习,多次小批量训练时,逐步更新极值
|
|
|
|
|
|
|
|
预处理针对所有数据,包括但不限于训练集,预测集,验证集等
默认训练集的数据包含了业务所需要的全部,
预测集的本意是未来出现的数据,虽然未出现,但它的规律与训练集的规律是一样的
用于归一化的min,max,mean,std等都来自训练集,
将来验证,预测的时候,第一步就是要对数据做同样的预处理
因此,预处理的数据,要做成统一的接口,与模型训练是分开的,是针对所有数据的
|
import numpy as np
np.random.seed(73)
#向量默认为列向量
a1=np.random.randint(low=0,high=10,size=(7,1))
a2=np.random.randint(low=10,high=100,size=(7,1))
a3=np.random.randint(low=100,high=1000,size=(7,1))
#矩阵index=0即第1个维度为列,第2个维度为行
aa = np.concatenate((a1,a2,a3),axis=1)
aa
array([
[ 6, 81, 935],
[ 2, 63, 662],
[ 0, 17, 930],
[ 8, 62, 984],
[ 3, 53, 456],
[ 1, 63, 679],
[ 6, 56, 953]])
min_ = aa.min(axis=0) max_ = aa.max(axis=0) std_ = aa.std(axis=0) min_,max_,std_ ( array([ 0, 17, 456]), array([ 8, 81, 984]), array([ 2.76272566, 18.07806202, 186.85779816]))
#aa是2维矩阵,min_是与aa某个维度相同的向量,这就存在一个广播机制
aa - min_
array([[ 6, 64, 479],
[ 2, 46, 206],
[ 0, 0, 474],
[ 8, 45, 528],
[ 3, 36, 0],
[ 1, 46, 223],
[ 6, 39, 497]])
#这里存在两次广播机制
(aa - min_)/(max_ - min_)
array([[0.75 , 1. , 0.90719697],
[0.25 , 0.71875 , 0.39015152],
[0. , 0. , 0.89772727],
[1. , 0.703125 , 1. ],
[0.375 , 0.5625 , 0. ],
[0.125 , 0.71875 , 0.42234848],
[0.75 , 0.609375 , 0.94128788]])
mean_ = aa.mean(axis=0)
#这里存在3次广播机制,std+1e-6 也是一次
(aa-mean_)/(std_+1e-6)
array([[ 0.82734 , 1.35918481, 0.72323905],
[-0.620505 , 0.36350291, -0.73776499],
[-1.34442751, -2.18101748, 0.69648073],
[ 1.55126251, 0.30818725, 0.98547054],
[-0.25854375, -0.18965369, -1.8402076 ],
[-0.98246626, 0.36350291, -0.64678672],
[ 0.82734 , -0.02370671, 0.81956899]])
|
保留训练集的以下列信息,与模型一起交付: 连续型数据列:最大值,最小值,均值,标准差 离散型数据列:不重复值 以及个数 如果数据量大,需要分批计算后合并 ------------------------------------------------------------ |
|
|
|
|
开始训练的时候,模型的精度 在训练集上不断增加 在测试集上不断增加 并且训练集与测试集增长幅度近似 过拟合: 当训练到一定程度,训练集上的精度还在增加, 但测试集上的精度不再增加,甚至出现下降, 此时,模型把不通用的事物也学了,结果在测试集上并不应验...