国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

機器學習特征工程的最佳實踐

人工智能 機器學習
在我們的實驗中,我們觀察到特征工程顯著提升了模型的性能,尤其是在與 EDA 和業務目標緊密結合的情況下。通過與領域專家和業務利益相關者合作完善假設,我們有望實現進一步的改進。

特征工程是模型訓練之前運行的關鍵過程,因為輸入數據的質量直接決定了模型輸出的質量。

雖然深度學習模型擅長從圖像或文本等非結構化數據中自動學習特征,但明確的特征工程對于表格數據集仍然至關重要。

在本文中,云朵君將展示特征工程對回歸任務的影響,特別關注具有混合數字、分類和基于時間的特征的大型表格數據集。

什么是特征工程

特征工程是從原始數據中選擇、轉換和創建新特征以提高機器學習模型性能的過程。

它涉及使用領域知識從數據中提取最相關的信息,并以適合所選機器學習算法的方式表示它。

特征工程的好處

精心設計的特征可以顯著提高模型的預測能力,即使是簡單的模型也可以掌握復雜的關系,因為它可以:

  • 降低數據稀疏性:大多數現實世界的數據集都是稀疏的,包含許多零值和缺失值。特征工程可以整合信息并創建更密集的表示,使模型更容易學習。
  • 處理多種數據類型: 原始數據有多種格式,例如數值、分類、文本和時間。特征工程將這些類型轉換為模型可以處理的數字格式。
  • 解決數據噪聲和異常值:特征工程減輕了噪聲數據和異常值的影響,從而產生了更穩健的模型。

當我們可以將特征格式化為直接對應于問題域中的有意義的概念時,它可以使模型的決策更具解釋性、準確性和穩健性。

特征工程中的常用技術

一些常見的特征工程技術包括:

對數轉換

  • 將對數應用于數值特征。
  • 可以使分布更加對稱,使模型呈現正態分布。
  • 最佳情況:處理傾斜的數值特征。

多項式特征創建

  • 通過將現有特征提升到冪(例如 x2、x3)來生成新特征。
  • 創建交互項(例如,x?y)。
  • 最佳情況:捕捉非線性關系。

分箱(離散化)

  • 將連續的數值分組到箱中。
  • 可以減少微小波動的影響并使關系更加線性。
  • 最佳情況:用線性模型處理非線性關系,數據包含顯著的異常值或偏差。

基于時間的特征

  • 提取星期幾、月份、年份、小時、季度,甚至更復雜的特征,如“是周末”、“是假期”。
  • 計算事件之間的時間差。
  • 最佳情況:季節性影響預測。

工作流程

雖然沒有一刀切的方法,但特征工程的一般工作流程通常涉及從定義問題和成功指標開始的整個項目生命周期。

在本文中,我將演示第 1 階段和第 2 階段(圖中的藍色框),特別關注特征工程。

圖:具有特征工程的機器學習項目工作流程圖:具有特征工程的機器學習項目工作流程


對于第 3 階段,“通過機器學習實現準確性”涵蓋了泛化的核心概念,假設了損失的根本原因。!

第一階段:基礎

1. 問題

對于在線零售業務,了解未來客戶支出對于營銷、庫存管理和戰略規劃至關重要。

讓我們想象這樣一個場景:企業在銷售增長方面苦苦掙扎,尋找可行的見解。

2. 成功指標

我選擇平均絕對誤差 (MAE)作為主要指標,因為它對偏斜數據具有穩健性(稍后,在 EDA 期間,我將討論目標變量:銷售額的數據分布)并選擇MSE作為支持指標。

3. EDA 和特征工程

這是初始階段數據準備的基礎部分。

為了演示,我將使用來自加州大學歐文分校機器學習庫的在線零售數據:直接在@公眾號:數據STUDIO 原文文末留言需要即可,云朵君將回復給大家:

圖:變量表圖:變量表

import os
import pandas as pd

df = pd.read_csv(csv_file_path)
df.info()

圖片圖片

該數據集有541,909 個數據點,具有八個特征:

已加載數據集已加載數據集

實際上,這些數據可以是簡單的 Excel 表,也可以是存儲在云服務器上的信息,或者我們可以組合多個數據源。

數據清理

在進入 EDA 之前,我將按照一般原則清理數據:

  • 保留數字類型: 如果某列應為數字(例如quantity、unitprice、customerid),請保留該列。使用數字類型進行數字運算會更加高效且更有意義。
  • 混合/字符串數據的對象類型: Pandas 中的dtypeobject指的是包含混合類型或主要為字符串的列。如果某列確實包含數字和字符串的混合,object則 dtype 為默認值。
  • 各種缺失數據: Pandas 會處理NaN缺失的數字數據和None缺失的對象數據。然而,當我們讀取數據時,缺失值可能表示為空字符串、特定文本(例如“N/A”),甚至只是空格,而 Pandas 可能無法自動將其解釋為NaN。我們需要識別這些值。

識別 NaN

對于具有混合數據類型的列,我將潛在的缺失值(如空格或“nan”字符串)轉換為NumPy’s NaN。

這對于后期的準確歸集是一個至關重要的準備。

import numpy as np 

obj_cols = [ 'invoiceno' , 'stockcode' , 'country' , 'invoicedate' ] 

# 列出潛在的 NaN 值
null_vals = [ '' , 'nan' , 'N/A' , None , 'na' , 'None' , 'none' ] 
removed_null = { item: np.nan for item in null_vals } 

for col in obj_cols: 
    df[col].replace(replaced_null, inplace= True ) 

df.info()

圖片圖片

現在,識別每列中缺失的數據(NumPy 的 NaN):

對于i, col in  enumerate (df.columns): 
    unique_num = df[col].nunique() 
    nan_num = df[col].isna(). sum () 
    print(f'{i}. {col} - {unique_num:,} data points (missing data: {nan_num:,})')

圖:每列中發現的缺失值圖:每列中發現的缺失值

處理description和customerid列中缺失的數據:

  • 我決定刪除description列,因為假設它對未來預測的影響有限。 注意:文本數據對于產品因式分解可能很有價值,尤其是在缺失部分比較有限的情況下。
  • 我保留了原有的customerid列來評估唯一用戶的影響,同時引入了一個新的is_registered二分類列(1:已注冊,0:未注冊),該列對應于每個客戶 ID,假設沒有 ID 的客戶未注冊。 注意:customerid這會導致基數過高。稍后我將使用二分類編碼來解決這個問題。
# 工程化之前復制基礎數據集
df_rev = df.copy() 

df_rev = df_rev.drop(columns='description')
df_rev['is_registered'] = np.where(df_rev['customerid'].isna(), 0, 1)

轉換數據類型

最后,考慮到潛在的特征工程,我轉換了invoicedate和 customerid的數據類型:

import pandas as pd

df_rev['invoicedate'] = pd.to_datetime(df_rev['invoicedate'])
df_rev['customerid'] = df_rev['customerid'].astype('Int64')
df_rev.info()

添加了第 7 列。第 5 列的數據類型已更新。添加了第 7 列。第 5 列的數據類型已更新。

探索性數據分析(EDA)和特征工程

清理完數據集后,我們現在可以轉到 EDA。

探索性數據分析 (EDA) 是一種專注于總結和可視化數據以了解其主要特征的數據分析技術。

從技術角度來說,我們可以執行無數次 EDA,尤其是在復雜數據集上。但我們的主要重點是揭示需要設計哪些特征,并深入了解數據預處理,從而提升模型性能。

任何其他分析都應該由模型本身處理,因為真正的底層模式要么太微妙,要么太復雜,我們無法手動發現。

因此,EDA 成為模型確定哪些分析(例如隱藏趨勢、群體差異)值得轉化為預測的第一步。

EDA 必須包含:

  1. 用于理解數據的基本分析(以單變量為重點) ;
  2. 基于與項目目標直接相關的假設進行的項目特定分析(以雙變量為重點)

1)基本分析(單變量重點)

這個初始階段是通過單獨分析每個變量來從根本上了解數據集。

首先,為了準備 EDA,我從列中提取了year、month,并按以下順序對數據進行排序:day_of_week,invoicedate,invoicedate

df_rev['invoicedate'] = pd.to_datetime(df_rev['invoicedate'])
df_rev['year'] = df_rev['invoicedate'].dt.year
df_rev['year_month'] = df_rev['invoicedate'].dt.to_period('M')
df_rev['month_name'] = df_rev['invoicedate'].dt.strftime('%b')
df_rev['day_of_week'] = df_rev['invoicedate'].dt.strftime('%a')
df_rev = df_rev.sort_values('invoicedate')

還推出了sales銷售分析專欄:

df_rev['sales'] = df_rev['quantity'] * df_rev['unitprice']

數據集如下:

圖片圖片

添加了第 8 列至第 12 列添加了第 8 列至第 12 列

了解數據分布

我繪制了數值特征的 PDF 和分類特征的直方圖,以識別異常值、傾斜和重尾等特征。

盡管真實的數據分布過于復雜而難以完全掌握,但分析對于有效的預處理和模型選擇至關重要。

數值列的 PDF

unitprice 和sales都是稀疏的,并且尾部嚴重,存在顯著的異常值。我將使用MAE作為評估指標,因為它對傾斜數據具有較好的魯棒性。

圖:unitprice和sales的PDF圖:unitprice和sales的PDF

圖:unitprice和sales的PDF

  • unitprice:最大值:38,970.0,最小值:-11,062.1,平均值:4.6,標準差:96.8
  • sales:最大值:168,469.6,最小值:-168,469.6,平均值:18.0,標準差:378.8

分類特征直方圖

invoiceno、year_month和day_of_week均勻分布:

圖片圖片

圖片圖片

圖片圖片

stockcode左側有一個峰值,右側有一個長尾,而quantity和country則顯示出退化分布,其中數據集中在幾個類別中:

圖片圖片

圖片圖片

圖片圖片

is_registered和year結果也是二分類的:

圖片圖片

圖片圖片

在此基礎上,我將進行針對特定項目的 EDA,以找出額外的特征工程機會。

2)項目特定的EDA

此階段深入研究,特別是尋找變量之間的關系,特別是潛在特征和目標變量之間的關系:sales。

首先,我將根據“銷售增長”這一挑戰的潛在解決方案提出三個假設。實際上,可以利用商業和專家的見解來完善這些假設。

假設1

“銷售趨勢是由一周中的某天或一個月中的某天決定的。”

鑒于數據集有限的 13 個月的銷售數據(二進制year),我關注較短的趨勢周期。

  • 需要設計的潛在特性is_weekend:day_of_month
  • 潛在商業解決方案:順應趨勢的大量促銷。

假設2

“產品銷售受時間和價格點驅動。”

需要設計的潛在特性:

  • unit_price_bin:unitprice離散化為“低”、“中”、“高”類別,直接解決價格影響和非線性。
  • product_avg_quantity_last_month:計算每個產品quantity在上一日歷月的平均銷量stockcode并獲取近期產品的受歡迎程度。
  • product_sales_growth_last_month:從 2 個月前到上個月stockcode的銷售額百分比變化,以確定流行產品。

潛在的商業解決方案:

  • 動態定價(通過促銷時機預測最佳價格點的模型)。
  • 定制產品推薦(預測產品因素相似性的模型)。

假設3

“活躍的顧客往往會購買更多商品,從而促進銷售。”

需要設計的潛在特性:

  • customer_recency_days:預測日期(上個月底)與客戶上次購買日期之間的天數,以評估近期購買的可能性。
  • customer_total_spend_ltm:客戶過去三個月產生的總銷售收入。這是對客戶近期貨幣價值的直接衡量。
  • customer_freq_ltm:過去三個月內客戶開具的唯一發票總數。這是直接影響銷售額的參與度指標之一。

潛在的商業解決方案:

  • 分層客戶忠誠度計劃(預測唯一用戶保留時間的模型)
  • 營銷媒體組合優化(預測新客戶價值的模型)

現在,執行 EDA 并決定要設計哪些特性。

假設 1

“銷售趨勢受一周中的某天或一個月中的某天的影響。”

除了11月的峰值之外,按月和按周劃分的銷售趨勢沒有出現明顯的模式。因此,我選擇不添加基于此假設的其他特征。

圖:按月份和星期幾劃分的銷售趨勢圖:按月份和星期幾劃分的銷售趨勢

假設2

“產品銷售受時間和價格點驅動。”

對于unit_price_bin,幾乎所有月份的三個價格區間的中線都接近于零。所有區間的四分位距 (IQR) 也很短,這表明 25-75 百分位數數據落在一個非常小的低量范圍內。

然而,我們可以看到異常值占據了主導地位,形成了明顯的分層。

因此,我決定添加特征unit_price,同時保留原有的粒度,使用箱內的精確值來預測數量。

圖:按價格范圍(低、中、高)劃分的月銷售總量及中位數和四分位距圖:按價格范圍(低、中、高)劃分的月銷售總量及中位數和四分位距

添加unit_price_bin到最終數據集:

import pandas as pd

# df_fin 將成為我們模型訓練的主要數據
df_fin = df_rev.copy() 

# 創建臨時數據
_df_prod_month_agg = df_fin.copy().groupby(['stockcode', 'year_month']).agg(
    prod_total_monthly_quantity=('quantity', 'sum'),
    prod_ave_monthly_price=('unitprice', 'mean')
).reset_index().sort_values(by=['stockcode', 'year_month'])

_df_prod_month_agg['unit_price_bin'] = pd.qcut(
    _df_prod_month_agg['prod_ave_monthly_price'],
    q=3,
    labels=['low', 'mid', 'high'],
    duplicates='drop'
)

_df_prod_bin_per_stockcode = _df_prod_month_agg.groupby('stockcode')['unit_price_bin'].agg(
    lambda x: x.mode()[0] ifnot x.mode().empty elseNone
).reset_index()

# 合并到主數據集 (df_fin)
df_fin = pd.merge(
    df_fin,
    _df_prod_bin_per_stockcode[['stockcode', 'unit_price_bin']],
    notallow='stockcode',
    how='left'
)

df_fin.info()

添加了第 13 列添加了第 13 列

product_avg_quantity_last_month也顯示出非常強的正相關性,這可以作為一個動量特征,表明上個月銷量好的產品本月也容易銷量好。我會添加這個特征。

圖:本月和上個月銷售的平均產品數量圖:本月和上個月銷售的平均產品數量

添加product_avg_quantity_last_month到最終數據集(也處理插補):

import pandas as pd

_df_prod_month_agg['product_avg_quantity_last_month'] = _df_prod_month_agg.groupby('stockcode')['prod_total_monthly_quantity'].shift(1)
_df_prod_last_month_agg = _df_prod_month_agg.groupby('stockcode')['product_avg_quantity_last_month'].mean().reset_index()
df_fin = pd.merge(
    df_fin,
    _df_prod_last_month_agg [['stockcode', 'product_avg_quantity_last_month']],
    notallow='stockcode',
    how='left'
)

# 缺失數據意味著在該期限內沒有售出任何產品。用零進行插補。
df_fin['product_avg_quantity_last_month'] = df_fin['product_avg_quantity_last_month'].fillna(value=0)
df_fin.info()

添加了第 14 列添加了第 14 列

另一方面,product_sales_growth_last_month它沒有表現出很強的線性/單調關系。考慮到這個特征的預測能力有限,我選擇不添加它。

圖:月度產品數量與上月銷售額增長率圖:月度產品數量與上月銷售額增長率

假設3

“活躍顧客傾向于購買更多產品并對銷售做出貢獻。”

customer_recency_days表明新近度較低的客戶(最近的購買,例如 x < 60 天)往往表現出更高的月銷售收入,表明呈反比關系(圖中紅色虛線)。

我將添加此特征來預測每月的銷售收入。

圖:月銷售額與客戶最近消費天數圖:月銷售額與客戶最近消費天數

添加customer_recency_days到數據集:

import pandas as pd 

# 創建臨時數據集
_df_all_customers_year_month = pd.MultiIndex.from_product(
    [df_fin['customerid'].unique(), df_fin['year_month'].unique()], # type: ignore
    names=['customerid', 'year_month']
).to_frame(index=False).sort_values(by=['customerid', 'year_month']).reset_index(drop=True)

_df_customer_monthly_agg = df_fin.copy().groupby(['customerid', 'year_month']).agg(
    monthly_sales=('sales', 'sum'),
    monthly_unique_invoices=('invoiceno', 'nunique'),
    monthly_last_purchase_date=('invoicedate', 'max')
).reset_index()

_df_cus = _df_all_customers_year_month.merge(_df_customer_monthly_agg, notallow=['customerid', 'year_month'], how='left').sort_values(by=['customerid', 'year_month'])

# 添加時間戳
_df_cus['pfin_last_purchase_date'] = _df_cus.groupby('customerid')['monthly_last_purchase_date'].shift(1)
_df_cus['invoice_timestamp_end'] = _df_cus['year_month'].dt.end_time

# 計算新近天數
_df_cus['customer_recency_days'] = (_df_cus['invoice_timestamp_end'] - _df_cus['pfin_last_purchase_date']).dt.days

# 合并和估算
df_fin['customer_recency_days'] = _df_cus['customer_recency_days']

max_recency = _df_cus['customer_recency_days'].max()
df_fin['customer_recency_days'] = df_fin['customer_recency_days'].fillna(value=max_recency + 30)

df_fin.info()

添加了第 15 列添加了第 15 列

customer_total_spend_ltm顯示出客戶過去三個月的總支出與其當前月銷售收入之間存在明顯的正相關關系。這表明,過去支出越高,當前收入就越高,這是一個非常有效的預測特征。我會添加這個特征。

圖:過去三個月的月銷售額與客戶總支出圖:過去三個月的月銷售額與客戶總支出

添加customer_total_spend_ltm:

_df_cus['customer_total_spend_ltm'] = _df_cus.groupby('customerid')['monthly_sales'].rolling(window=3, closed='left').sum().reset_index(level=0, drop=True)

df_fin['customer_total_spend_ltm'] = _df_cus['customer_total_spend_ltm']
df_fin['customer_total_spend_ltm'] = df_fin['customer_total_spend_ltm'].fillna(value=0)

df_fin.info()

添加了第 16 列添加了第 16 列

customer_freq_ltm還展示了客戶過去三個月的購買頻率與其當前月銷售收入之間的正相關關系。過去三個月擁有更多獨立發票的客戶往往能帶來更高的月收入。我也會添加此特征。

圖:過去三個月的月銷售額與客戶頻率圖:過去三個月的月銷售額與客戶頻率

添加customer_freq_ltm:

_df_cus['customer_freq_ltm'] = _df_cus.groupby('customerid')['monthly_unique_invoices'].rolling(window=3, closed='left').sum().reset_index(level=0, drop=True)

df_fin['customer_freq_ltm'] = _df_cus['customer_freq_ltm']
df_fin['customer_freq_ltm'] = df_fin['customer_freq_ltm'].fillna(value=0)

df_fin.info()

添加了第 17 列添加了第 17 列

對缺失值的最終檢查

特征工程完成后,我檢查了剩余的缺失值,在更新后的數據集中的stockcode、quantity、unit_price_bin和country列中發現了五個缺失項:

df_fin.isna().sum ()

我會在編碼過程中處理丟失的客戶 ID)我會在編碼過程中處理丟失的客戶 ID)

我會在編碼過程中處理丟失的客戶 ID)

逐一檢查這些缺失的項目并進行估算。

注意:鑒于 540k+ 個樣本中最多只有 20 個有缺失值,因此可以選擇按行刪除(即從數據集中刪除這些樣本)。

對于stockcode和unit_price_bin,樣本中缺失stockcode或unit_price_bin的其他值看起來是合法的。

我用“unknown”(字符串)和“low”換替換了stockcode和unit_price_bin中的缺失的值:

df_null = df_fin[df_fin['stockcode'].isnull()] 
df_null.head().transpose()

圖片圖片

df_fin['stockcode'] = df_fin['stockcode'].fillna(value='unknown')
df_fin['unit_price_bin'] = df_fin['unit_price_bin'].fillna(value='low')

country采取同樣的過程,和列中的缺失值quantity分別用其眾數值和銷售額/單價值填充:

import numpy as np

df_fin['country'] = df_fin['country'].fillna(value=df_fin['country'].mode().iloc[0]) 
df_fin['quantity'] = df_fin['quantity'].fillna(value=np.floor(df_fin['sales'] / df_fin['unitprice']))

最后,轉換數據類型以最終確定數據集:

df_fin['year_month'] = df_fin['year_month'].dt.month
df_fin['invoicedate'] = df_fin['invoicedate'].astype(int) / 10 ** 9
df_fin = df_fin.drop(columns=['month_name'], axis='columns')

df_fin.info()

最終數據集最終數據集

該數據集的最終版本有541,909 個樣本,包含17 個特征:

cat_cols = [ 
    'invoiceno' , 
    'stockcode' , 
    'quantity' , 
    'customerid' , 
    'country' , 
    'year' , 
    'year_month' , 
    'day_of_week' , 
    'is_registered' , 
    'unit_price_bin' , 
    'customer_recency_days' , 
] 
num_cols = [ 
    'unitprice' , 
    'product_avg_quantity_last_month' , 
    'customer_total_spend_ltm' , 
    'customer_freq_ltm' , 
    'invoicedate'
 ] 

target_col = 'sales'

回顧——第一階段的特征工程

根據 EDA 結果,我添加了 11 個特征:

  • 來自單變量EDA :is_registered,year,year_month,month_name,day_of_week,sales
  • 來自雙變量EDA:unit_price_bin,product_avg_quantity_last_month,customer_recency_days,customer_total_spend_ltm,customer_freq_ltm

并刪除了一個特征:description由于其缺失值量較大且對預測的影響有限。

一文帶你用sklearn做特征工程

一文詳盡特征工程與數據預處理

4. 模型選擇

鑒于數據集復雜且龐大,我選擇了以下三種模型:

  • 彈性網絡:正則化線性回歸模型,適合作為線性可分數據的基線。
  • 隨機森林:一種能夠捕捉復雜、非線性關系的強大機器學習模型。
  • 深度前饋網絡:一種深度學習模型,可作為非線性可分離數據的強大基礎。為了有效地管理大型數據集,我使用了PyTorch庫。

原理+代碼,總結了 11 種回歸模型

萬字長文,演繹八種線性回歸算法最強總結!

總結了九種機器學習集成分類算法(原理+代碼)

理論+股市數據實戰,總結了五種常用聚類分析算法

總結了17個機器學習的常用算法!

5. 在預處理數據上訓練模型

首先,我將數據集分成所有模型的訓練集、驗證集和測試集。

我故意沒有對數據集進行打亂,以保留其時間順序。

from sklearn.model_selection import train_test_split

target_col = 'sales'
 X = df_fin.copy().drop(columns=target_col) 
y = df_fin.copy()[target_col] 

test_size = 50000
 X_tv, X_test, y_tv, y_test = train_test_split(X, y, test_size=test_size, random_state= 42 ) 
X_train, X_val, y_train, y_val = train_test_split(X_tv, y_tv, test_size=test_size, random_state= 42 )

每個模型對預處理的需求不同:

圖:按模型劃分的數據預處理要求圖:按模型劃分的數據預處理要求

因此,我將準備用于分別訓練每個模型的數據集。

彈性網絡

彈性網絡需要在縮放和編碼的數據集上進行訓練。

對于數值特征,我應用RobustScaler來處理我們在 EDA 期間發現的顯著異常值。

對于分類特征,我應用了BinaryEncoder來限制維度的增加,同時用零替換

customerid列中的缺失值:

from sklearn.preprocessing import RobustScaler
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from category_encoders import BinaryEncoder


# num
 num_transformer = Pipeline(steps=[ 
    ( 'scaler' , RobustScaler(with_centering= True , with_scaling= True )) 
]) 

# cat
 cat_transformer = Pipeline(steps=[ 
    ( 'encoder' , BinaryEncoder(cols=cat_cols, handle_missing= '0' )) 
]) 

# 定義一個預處理器
preprocessor_en = ColumnTransformer( 
    transforms=[ 
        ( 'num' , num_transformer, num_cols), 
        ( 'cat' , cat_transformer, cat_cols) 
    ], 
    remainder= 'passthrough' , 
) 

# 變換
X_train_processed = preprocessor_en.fit_transform(X_train) 
X_val_processed = preprocessor_en.transform(X_val) 
X_test_processed = preprocessor_en.transform(X_test) 

# 啟動并訓練模型
from sklearn.linear_model import ElasticNet 
elastic_net = ElasticNet( 
    alpha= 1 ,             # 正則化的總強度
    l1_ratio= 0.5 ,        # l1 到 l2 的比例 = 1:1
     fit_intercept= True , # 通過計算 y 截距進行擬合
    precompute= False ,    # 不使用預先計算的 Gram 矩陣
    max_iter= 5000 ,       # 1,000 個 epochs
     copy_X= True ,         # 擬合前復制 X
     tol= 1e-5 ,            # tol 停止迭代
    random_state= 42 ,     # 隨機數生成器的種子
    warm_start= False ,    # 忽略前一次擬合調用中的解
    positive= False ,      # 可以同時    選擇負系數和正系數"cyclic" # 循環地逐個更新系數(與隨機相比) ).fit(X_train_processed, y_train)

隨機森林

對于隨機森林,我們可以跳過縮放部分:

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestRegressor
from category_encoders import BinaryEncoder


# 定義預處理器
cat_transformer = Pipeline(steps=[ 
    ( 'encoder' , BinaryEncoder(cols=cat_cols, handle_missing= '0' )) 
]) 

preprocessor_rf = ColumnTransformer( 
    transforms=[ 
        ( 'cat' , cat_transformer, cat_cols) 
    ], 
    remainder= 'passthrough' , 
) 


# 變換
X_train_processed = preprocessor_rf.fit_transform(X_train) 
X_val_processed = preprocessor_rf.transform(X_val) 
X_test_processed = preprocessor_rf.transform(X_test) 


# 啟動并訓練模型
random_forest = RandomForestRegressor(
    n_estimators=1000,
    criterinotallow="squared_error",
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    min_weight_fraction_leaf=0,
    max_features='sqrt',
    max_leaf_nodes=None,
    min_impurity_decrease=1e-10,
    bootstrap=True,
    oob_score=True,
    n_jobs=-1,
    random_state=42,
    verbose=0,
    warm_start=False,
    ccp_alpha=0,
    max_samples=None,
).fit(X_train_processed, y_train)

DFN

DFN 需要縮放和編碼。對于數值特征,我使用了StandardScaler,因為它在處理復雜數據方面具有良好的魯棒性。之后,數據集被轉換為TensorDataset:

import torch
from torch.utils.data import DataLoader, TensorDataset
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from category_encoders import BinaryEncoder

num_transformer = Pipeline(steps=[('scaler', StandardScaler())])
cat_transformer = Pipeline(steps=[('encoder', BinaryEncoder(cols=cat_cols, handle_missing='0'))])

# 定義一個預處理器
preprocessor_dfn = ColumnTransformer( 
    transforms=[ 
        ( 'num' , num_transformer, num_cols), 
        ( 'cat' , cat_transformer, cat_cols) 
    ], 
    remainder= 'passthrough'
 ) 

# 轉換
X_train_processed_dfn = preprocessor_dfn.fit_transform(X_train) 
X_val_processed_dfn = preprocessor_dfn.transform(X_val) 
X_test_processed_dfn = preprocessor_dfn.transform(X_test) 


# 將 NumPy 數組轉換為 PyTorch 張量
X_train_tensor = torch.tensor(X_train_processed_dfn, dtype=torch.float32) 
X_val_tensor = torch.tensor(X_val_processed_dfn, dtype=torch.float32) 
X_test_tensor = torch.tensor(X_test_processed_dfn, dtype=torch.float32) 

# 轉換為 1D 張量
y_train_tensor = torch.tensor(y_train.values, dtype=torch.float32).view(- 1 , 1 ) 
y_val_tensor = torch.tensor(y_val.values, dtype=torch.float32).view(- 1 , 1 ) 
y_test_tensor = torch.tensor(y_test.values, dtype=torch.float32).view(- 1 , 1 ) 

# 轉換為 TensorDataset
 train_dataset = TensorDataset(X_train_tensor, y_train_tensor) 
val_dataset = TensorDataset(X_val_tensor, y_val_tensor) 
test_dataset = TensorDataset(X_test_tensor, y_test_tensor) 

# 批處理
batch_size = 32
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=False)
val_loader = DataLoader(dataset=val_dataset, batch_size=batch_size, shuffle=False)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=False)

然后,啟動模型:

import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

classDFN(nn.Module):
    def__init__(self, input_dim):
        super(DFN, self).__init__()
        self.fc1 = nn.Linear(input_dim, 32)
        self.relu1 = nn.ReLU()
        self.dropout1 = nn.Dropout(0.1)
        self.fc2 = nn.Linear(32, 16)
        self.relu2 = nn.ReLU()
        self.dropout2 = nn.Dropout(0.1)
        self.fc3 = nn.Linear(16, 1)
    defforward(self, x):
        x = self.fc1(x)
        x = self.relu1(x)
        x = self.dropout1(x)
        x = self.fc2(x)
        x = self.relu2(x)
        x = self.dropout2(x)
        x = self.fc3(x)
        return x

input_dim = X_train_processed_dfn.shape[1]

device = torch.device('cuda'if torch.cuda.is_available() else'cpu')

model = DFN(input_dim).to(device)
criterion = nn.L1Loss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

訓練模型:

from sklearn.metrics import mean_squared_error, mean_absolute_error

num_epochs = 100
best_val_loss = float('inf')
patience = 10
patience_counter = 0
min_delta = 1e-4
history = {
'train_loss': [],
'val_loss': [], 
'train_mse': [], 
'val_mse': [], 
'train_mae': [], 
'val_mae': []
}

for epoch in range(num_epochs):
    model.train()

    running_train_loss = 0.0
    all_train_preds = []
    all_train_targets = []

    for batch_idx, (data, target) in enumerate(train_loader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        outputs = model(data)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()
        running_train_loss += loss.item() * data.size(0)
        all_train_preds.extend(outputs.detach().cpu().numpy())
        all_train_targets.extend(target.detach().cpu().numpy())

    epoch_train_loss = running_train_loss / len(train_dataset)
    train_mse = mean_squared_error(np.array(all_train_targets), np.array(all_train_preds))
    train_mae = mean_absolute_error(np.array(all_train_targets), np.array(all_train_preds))
    
    model.eval()
    running_val_loss = 0.0
    all_val_preds = []
    all_val_targets = []

    with torch.no_grad():
        for data, target in val_loader:
            data, target = data.to(device), target.to(device)
            outputs = model(data)
            loss = criterion(outputs, target)
            running_val_loss += loss.item() * data.size(0)
            all_val_preds.extend(outputs.cpu().numpy())
            all_val_targets.extend(target.cpu().numpy())

    epoch_val_loss = running_val_loss / len(val_dataset)
    val_mse = mean_squared_error(np.array(all_val_targets), np.array(all_val_preds))
    val_mae = mean_absolute_error(np.array(all_val_targets), np.array(all_val_preds))

    history['train_loss'].append(epoch_train_loss)
    history['val_loss'].append(epoch_val_loss)
    history['train_mse'].append(train_mse)
    history['val_mse'].append(val_mse)
    history['train_mae'].append(train_mae)
    history['val_mae'].append(val_mae)

結果

平均輔助能量吸收

  • 彈性網絡:訓練:19.773 → 驗證:18.508
  • 隨機森林:訓練:4.147 → 驗證:10.551
  • 深度前饋網絡:訓練:10.570 → 驗證:10.987

Elastic Net 的泛化能力良好(訓練集 19.77,驗證集 18.51),但平均誤差最高。其預測與實際銷售額的偏差約為18.50 美元至 19.77 美元。

隨機森林過擬合嚴重(訓練集 4.15,驗證集 10.55)。平均而言,其對新數據的預測偏差約為10.55 美元。

深度前饋網絡 (DFN)表現出了出色的泛化能力(訓練集 10.57,驗證集 10.99),并且在未見數據上實現了較低的平均誤差。其預測偏差約為10.99 美元。

總而言之,隨機森林是表現最好的模型,但 DFN 在泛化方面也表現出色。

圖片圖片

圖片圖片

圖片圖片

圖:實際銷售額與預測銷售額(左:彈性網絡,中:隨機森林),DFN 的損失歷史記錄(右)

第二階段:迭代改進

第一階段的結果表明,這三個模型的泛化能力仍有提升空間。

我對銷售值應用了對數轉換,以便為模型的目標變量創建更加對稱的分布。

為了區分退款(sales列中的負銷售額)和正銷售額,我創建了一個is_return二分類token(1 表示退款,0 表示銷售額)。這樣一來,sales列就可以只關注正銷售額。

從數學上講,對負值取對數的結果是NaN。因此,我先用零替換負銷售額,然后應用拉普拉斯平滑法。這也能避免對數銷售額中出現負無窮值。

import numpy as np 

# 使用新數據集
df_fin_rev = df_fin.copy() 

# 添加 is_return 標志
df_fin_rev['is_return'] = (df_fin_rev['sales'] < 0).astype(int) 

# sales 列中的零值或正值
df_fin_rev[ 'sales' ] = df_fin_rev[ 'sales' ].apply( lambda x: max (x, 0 )) 

# 在取對數之前應用拉普拉斯平滑
alpha = 1
df_fin_rev['sales'] = np.log(df_fin_rev['sales'] + alpha)

df_fin_rev.info()

添加第 17 列。變換第 11 列。添加第 17 列。變換第 11 列。

在確保數據集中除 customerid 列外不存在缺失值后:

df_fin_rev.isna().sum ()

圖片圖片

我使用相同的預處理步驟和超參數重新訓練了模型:

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.metrics import mean_squared_error, mean_absolute_error
from category_encoders import BinaryEncoder

# 創建數據集
X = df_fin_rev.copy().drop(columns=target_col)
y = df_fin_rev.copy()[target_col]

test_size = 50000
X_tv, X_test, y_tv, y_test = train_test_split(X, y, test_size=test_size, random_state=42)
X_train, X_val, y_train, y_val = train_test_split(X_tv, y_tv, test_size=test_size, random_state=42)

# 預處理
num_transformer = Pipeline(steps=[('scaler', StandardScaler())])
cat_transformer = Pipeline(steps=[('encoder', BinaryEncoder(cols=cat_cols, handle_missing='0'))])
preprocessor_en = ColumnTransformer(
    transformers=[
        ('num', num_transformer, num_cols),
        ('cat', cat_transformer, cat_cols)
    ],
    remainder='passthrough'
)
X_train_processed_en = preprocessor_en.fit_transform(X_train)
X_val_processed_en = preprocessor_en.transform(X_val)
X_test_processed_en = preprocessor_en.transform(X_test)

# 模型訓練
elastic_net.fit(X_train_processed_en, y_train)

# 預測 (對數銷售額)
y_pred_train = elastic_net.predict(X_train_processed_en) 
y_pred_val = elastic_net.predict(X_val_processed_en)
y_pred_test = elastic_net.predict(X_test_processed_en)

# 評估 - 對數銷售額 - 使用 MSE 進行評估
mse_train = mean_squared_error(y_train, y_pred_train)
mse_val = mean_squared_error(y_val, y_pred_val)
mse_test = mean_squared_error(y_test, y_pred_test)

# 評估 - 實際銷售額 - 使用 MAE 進行評估
mae_train_exp = mean_absolute_error(np.exp(y_train), np.exp(y_pred_train))
mae_val_exp = mean_absolute_error(np.exp(y_val), np.exp(y_pred_val))
mae_test_exp = mean_abolute_error(np.exp(y_test), np.exp(y_pred_test))

結果

使用記錄的銷售數據的MSE和實際值銷售的MAE來評估模型性能:

彈性網絡:

  • 對數銷售的 MSE:訓練集:1.133 → 1.132,泛化集:1.122
  • 實值銷售的 MAE:訓練集:15.825 → 14.714,泛化集:16.509

隨機森林:

  • 對數銷售額的 MSE:訓練集:0.020 → 0.175,泛化集:0.176
  • 實值銷售的 MAE:訓練集:4.135 → 7.187,泛化集:9.041

DFN:

  • 對數銷售額的 MSE:訓練集:1.079 → 0.165 泛化集:0.079
  • 實值銷售的 MAE:訓練集:5.644 → 5.016,泛化集:6.197

(基于 50,000 個測試樣本的概括。)

與第一階段相比,所有模型中實際銷售額的 MAE 都有所提高,這表明目標變量密度的重要性。

其中,DFN 在訓練集(5.64)和泛化集(6.20)中均表現出較低的 MAE,展現出最佳性能,表明其在復雜、大型數據集上的學習和泛化能力較強。其對未見數據的預測偏差約為6.20 美元。

Elastic Net表現出了極好的泛化能力,但其對未見數據的預測偏差為16.51 美元,是所有模型中偏差最大的,這表明其在處理復雜數據集時遇到了困難。

隨機森林表現出嚴重的過擬合,其較低的訓練 MAE(4.14)與較高的泛化 MAE(9.04)之間存在較大差距。該模型的下一步可以進行超參數調整,以收緊正則化變量和樹結構。

實驗總結

實驗表明,PyTorch 上的 DFN 在具有 EDA 期間識別的特征的轉換數據集上表現最佳。

回到業務解決方案的初始假設,我們可以將這一發現直接用于營銷媒體組合優化,例如,使 DFN 能夠預測新客戶的終身價值并優化對高價值客戶渠道的預算分配。

下一步,我們可以在第 2 階段進一步探索特征工程,或者進入第 3 階段調整超參數以完善結果。

寫在最后

特征工程不僅僅是數據操作;它是一種從原始數據中獲取強大洞察力并顯著提高模型解決當前問題的能力的戰略方法。

在我們的實驗中,我們觀察到特征工程顯著提升了模型的性能,尤其是在與 EDA 和業務目標緊密結合的情況下。通過與領域專家和業務利益相關者合作完善假設,我們有望實現進一步的改進。

通過投入時間和精力來制定有效的輸入,我們從根本上增強了模型的學習、概括和提供卓越預測性能的能力。

責任編輯:武曉燕 來源: 數據STUDIO
相關推薦

2021-05-10 16:41:19

機器學習人工智能IT

2024-06-13 09:12:38

2024-10-08 15:09:17

2024-10-28 15:52:38

機器學習特征工程數據集

2024-10-28 00:00:10

機器學習模型程度

2024-10-08 10:16:22

2018-07-23 15:35:17

機器學習特征工程技能數據科學

2016-04-12 17:12:29

機器學習數據清洗美團

2021-04-20 14:18:57

人工智能機器學習

2021-04-01 22:19:54

機器學習模型數據

2024-07-31 15:36:00

2014-04-18 10:58:44

AndroidAPI實踐

2017-02-05 17:10:41

機器學習深度學習框架

2018-10-05 23:26:00

機器學習算法數據

2021-01-26 09:46:59

PythonStacking機器學習

2019-06-25 10:09:42

Web攻擊機器學習網絡攻擊

2020-11-26 18:30:33

機器學習Kubernetes開發

2022-02-16 07:00:00

機器學習特征選擇過濾法

2025-01-20 09:21:00

2024-12-26 00:34:47

點贊
收藏

51CTO技術棧公眾號

影音先锋在线视频| 亚洲热线99精品视频| 国产a久久精品一区二区三区 | 欧美videos中文字幕| 免费人成在线不卡| 97久久中文字幕| 日韩精品视频无播放器在线看 | 欧美巨乳在线观看| 欧美日韩国产一区在线| 麻豆久久久久久| 亚洲尤物av| 国产亚洲欧美日韩精品一区二区三区| 在线观影网站| v天堂福利视频在线观看| 国产美女视频一区二区| 午夜视频精品| 国产精品国产三级国产三级人妇 | 日日碰狠狠添天天爽超碰97| 亚洲第一视频网| 国产精品传媒毛片三区| 国产视频一区二区三区在线观看| 轻轻色免费在线视频| 欧美精品黑人性xxxx| 国产亚洲电影| 无码人妻丰满熟妇区五十路百度| 精品福利一二区| 久久久久久久综合色一本| 欧美一区一区| 青春草国产视频| 欧美美女黄视频| 美女一区2区| 野外做受又硬又粗又大视频√| 在线影视一区二区三区| 天海翼精品一区二区三区| 免费观看成人在线视频| 欧美日韩成人在线观看| 国产精品一区二区免费不卡| 日韩电影大全网站| 午夜亚洲视频| 亚洲成人三级| 久久视频免费在线| 欧美日韩国产999| 精品久久久久久久久久久| 一本色道久久精品| 日韩精品麻豆| 97在线观看免费高清视频| 国产专区一区二区三区| 亚洲视频网站在线观看| 国产欧美日韩久久| 午夜精品剧场| yiren22亚洲综合| 日韩黄色网址| 影音先锋成人资源网站| 国产精品国模在线| 亚洲国产小视频在线观看| 国产日韩欧美制服另类| 91精品国产自产在线观看永久∴| av在线播放资源| 好男人社区在线视频| 午夜一区二区三区| 国产不卡视频在线| 日韩精品亚洲精品| 亚洲一区二三区| 加勒比av一区二区| 加勒比久久综合| 中文字幕乱码在线播放| 高清欧美精品xxxxx在线看| 免费国产成人看片在线| 国产专区欧美专区| 中文字幕免费精品一区高清| 欧美亚洲动漫另类| 国产精品网站在线播放| 视频一区二区不卡| 日韩大片在线观看| 91精品网站在线观看| 巨大荫蒂视频欧美大片| www.成人69.com| 400部精品国偷自产在线观看| 国产一区二区色| 综合136福利视频在线| 在线精品视频一区二区三四| 久久精品亚洲麻豆av一区二区| 国产日韩亚洲| 欧美日韩xxxx| 欧美性aaa| 欧美xxxx黑人又粗又长| 久久白虎精品| 丰满少妇被猛烈进入高清播放| 久久国产手机看片| 国产99在线|中文| 色偷偷888欧美精品久久久| 3d动漫精品啪啪1区2区免费| 一区二区三区蜜桃网| 91美女福利视频| 麻豆精品一区二区av白丝在线| 成人情趣视频网站| 日韩在线观看中文字幕| 日韩脚交footjobhd| 黄色软件在线| 日韩男人天堂| 日韩视频免费在线播放| 久久av喷吹av高潮av| 国产偷久久久精品专区| 国产精品白嫩美女在线观看| 久久国产视频网站| 亚洲男人的天堂在线播放| 欧美日韩二区三区| 午夜精品国产更新| 亚洲欧洲精品成人久久奇米网| 成人性生交大片免费看视频在线| 久久国产精品久久久久久电车| 久久精品国内一区二区三区水蜜桃 | silk一区二区三区精品视频| 美女100%一区| 粗大黑人巨茎大战欧美成人| 久久99久久| 亚洲高清福利| 天天综合入口| 开心丁香婷婷深爱五月| 免费一区二区三区在线观看| 欧美韩国日本在线| 免费拍拍拍网站| 在线一区高清| 亚洲日本精品国产第一区| 欧美福利一区二区三区| 国产欧美日本在线| 91视频国产一区| 国产欧美日韩亚洲精品| 国产精品久久久久久久7电影| 全亚洲最色的网站在线观看| 欧洲美女免费图片一区| 欧美亚洲激情在线| 97在线免费观看视频| 国内精品一区二区三区| 国内免费精品永久在线视频| 欧美极品第一页| 久久久女女女女999久久| 欧美成人激情图片网| 久久人体大胆视频| 久久久精品一区二区三区| 日韩在线中文字| 欧美精品在线观看| 欧美精品国产精品日韩精品| 7m第一福利500精品视频| 国产成人激情视频| 91在线中文字幕| 国产伦精品一区二区三区在线| 精品国产乱码久久久久软件| 日韩午夜视频在线观看| 一二三在线视频| 精品人妻一区二区三区四区在线| 日日碰狠狠躁久久躁婷婷| 国产三级三级看三级| 男捅女免费视频| 在线看中文字幕| 免费av网站在线观看| 国产理论在线| 亚洲综合在线电影| 99精品国产九九国产精品| 伊人精品综合| 成人久久电影| 亚洲综合不卡| 国产精品456| 国产欧美视频一区二区三区| 最近日韩中文字幕| 懂色av中文一区二区三区天美| 欧美羞羞免费网站| 精品久久人人做人人爱| 日韩在线观看网站| 日韩美女在线观看一区| 国产日韩欧美亚洲一区| 日韩视频一二三| 99免费视频| 国产粉嫩一区二区三区在线观看| 色呦呦网站在线观看| 日韩福利影视| 成人动漫免费在线观看| 99pao成人国产永久免费视频| 久久国产夜色精品鲁鲁99| 国产日产精品一区| 欧美视频在线看| 亚洲第一视频网站| 欧美精品在线免费播放| 91中文精品字幕在线视频| 一区二区三区不卡在线| 亚洲欧美另类动漫| 日本天堂在线| 日韩伦理在线一区| 欧美黄色影院| 久久国产精品久久久久久电车| 99久久精品国产精品久久| 亚洲国产成人va在线观看天堂| 欧美一区二区在线视频| 深夜福利日韩在线看| 国产精品视频999| 日韩精品久久久免费观看| 91日韩视频在线观看| 在线免费av网站| 国产麻豆一区二区三区| 欧美激情第二页|