Pipeline 和 Transformer

2021-01-18 人工智慧遇見磐創

掌握sklearn必須知道這三個強大的工具。因此,在建立機器學習模型時,學習如何有效地使用這些方法是至關重要的。

在深入討論之前,我們先從兩個方面著手:

Transformer:Transformer是指具有fit()和transform()方法的對象,用於清理、減少、擴展或生成特徵。簡單地說,transformers幫助你將數據轉換為機器學習模型所需的格式。OneHotEncoder和MinMaxScaler就是Transformer的例子。Estimator:Estimator是指機器學習模型。它是一個具有fit()和predict()方法的對象。我們將交替使用模型和Estimator這2個術語。該連結是一些Estimator的例子:https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html。

安裝

如果你想在你電腦上運行代碼,確保你已經安裝了pandas,seaborn和sklearn。我在Jupyter notebook中在python3.7.1中編寫腳本。

讓我們導入所需的庫和數據集。關於這個數據集(包括數據字典)的詳細信息可以在這裡找到(這個源實際上是針對R的,但是它似乎引用了相同的底層數據集):https://vincentarelbundock.github.io/Rdatasets/doc/reshape2/tips.html。

# 設置種子seed = 123# 為數據導入包/模塊import pandas as pdfrom seaborn import load_dataset# 為特徵工程和建模導入模塊from sklearn.model_selection import train_test_splitfrom sklearn.base import BaseEstimator, TransformerMixinfrom sklearn.preprocessing import OneHotEncoder, MinMaxScalerfrom sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipeline, FeatureUnionfrom sklearn.compose import ColumnTransformerfrom sklearn.linear_model import LinearRegression# 加載數據集df = load_dataset('tips').drop(columns=['tip', 'sex']).sample(n=5, random_state=seed)# 添加缺失的值df.iloc[[1, 2, 4], [2, 4]] = np.nandf

使用少量的記錄可以很容易地監控每個步驟的輸入和輸出。因此,我們將只使用數據集中5條記錄的樣本。

管道

假設我們想用smoker、day和time列來預測總的帳單。我們將先刪除size列並對數據進行劃分:

# 劃分數據X_train, X_test, y_train, y_test = train_test_split(df.drop(columns=['total_bill', 'size']), df['total_bill'], test_size=.2, random_state=seed)通常情況下,原始數據不是我們可以直接將其輸入機器學習模型的狀態。因此,將數據轉換為可接受且對模型有用的狀態成為建模的必要先決條件。讓我們做以下轉換作為準備:

用「missing」填充缺失值one-hot編碼以下完成這兩個步驟:

# 輸入訓練數據imputer = SimpleImputer(strategy='constant', fill_value='missing')X_train_imputed = imputer.fit_transform(X_train)# 編碼訓練數據encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)X_train_encoded = encoder.fit_transform(X_train_imputed)# 檢查訓練前後的數據print("******************** Training data ********************")display(X_train)display(pd.DataFrame(X_train_imputed, columns=X_train.columns))display(pd.DataFrame(X_train_encoded, columns=encoder.get_feature_names(X_train.columns)))# 轉換測試數據X_test_imputed = imputer.transform(X_test)X_test_encoded = encoder.transform(X_test_imputed)# 檢查測試前後的數據print("******************** Test data ********************")display(X_test)display(pd.DataFrame(X_test_imputed, columns=X_train.columns))display(pd.DataFrame(X_test_encoded, columns=encoder.get_feature_names(X_train.columns)))你可能已經注意到,當映射回測試數據集的列名時,我們使用了來自訓練數據集的列名。這是因為我更喜歡使用來自於訓練Transformer的數據的列名。但是,如果我們使用測試數據集,它將給出相同的結果。

對於每個數據集,我們首先看到原始數據,然後是插補後的輸出,最後是編碼後的輸出。

這種方法可以完成任務。但是,我們將上一步的輸出作為輸入手動輸入到下一步,並且有多個臨時輸出。我們還必須在測試數據上重複每一步。隨著步驟數的增加,維護將變得更加繁瑣,更容易出錯。

我們可以使用管道編寫更精簡和簡潔的代碼:

# 將管道與訓練數據匹配pipe = Pipeline([('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])pipe.fit(X_train)# 檢查訓練前後的數據print("******************** Training data ********************")display(X_train)display(pd.DataFrame(pipe.transform(X_train), columns=pipe['encoder'].get_feature_names(X_train.columns)))# 檢查測試前後的數據print("******************** Test data ********************")display(X_test)display(pd.DataFrame(pipe.transform(X_test), columns=pipe['encoder'].get_feature_names(X_train.columns)))

使用管道時,每個步驟都將其輸出作為輸入傳遞到下一個步驟。因此,我們不必手動跟蹤數據的不同版本。這種方法為我們提供了完全相同的最終輸出,但是使用了更優雅的代碼。

在查看了轉換後的數據之後,現在是在我們的示例中添加模型的時候了。讓我們從為第一種方法添加一個簡單模型:

# 輸入訓練數據imputer = SimpleImputer(strategy='constant', fill_value='missing')X_train_imputed = imputer.fit_transform(X_train)# 編碼訓練數據encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)X_train_encoded = encoder.fit_transform(X_train_imputed)# 使模型擬合訓練數據model = LinearRegression()model.fit(X_train_encoded, y_train)# 預測訓練數據y_train_pred = model.predict(X_train_encoded)print(f"Predictions on training data: {y_train_pred}")# 轉換測試數據X_test_imputed = imputer.transform(X_test)X_test_encoded = encoder.transform(X_test_imputed)# 預測測試數據y_test_pred = model.predict(X_test_encoded)print(f"Predictions on test data: {y_test_pred}")

我們將對管道方法進行同樣的處理:

# 將管道與訓練數據匹配pipe = Pipeline([('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False)), ('model', LinearRegression())])pipe.fit(X_train, y_train)# 預測訓練數據y_train_pred = pipe.predict(X_train)print(f"Predictions on training data: {y_train_pred}")# 預測測試數據y_test_pred = pipe.predict(X_test)print(f"Predictions on test data: {y_test_pred}")

你可能已經注意到,一旦我們訓練了一條管道,進行預測是多麼簡單。pipe.predict(X)對原始數據進行轉換,然後返回預測。也很容易看到步驟的順序。讓我們直觀地總結一下這兩種方法:

使用管道不僅可以組織和簡化代碼,而且還有許多其他好處,下面是其中一些好處:

微調管道的能力:當構建一個模型時,你可能需要嘗試不同的方法來預處理數據並再次運行模型,看看預處理步驟中的調整是否能提高模型的泛化能力。在優化模型時,微調不僅存在於模型的超參數中,而且存在於預處理步驟的實現中。考慮到這一點,當我們有一個統一了Transformer和Estimator的管道對象時,我們可以微調整個管道的超參數,包括使用GridSearchCV或RandomizedSearchCV的Estimator和兩個Transformer。更容易部署:在訓練模型時用於準備數據的所有轉換步驟在進行預測時也可以應用於生產環境中的數據。當我們訓練管道時,我們訓練一個包含數據轉換器和模型的對象。一旦經過訓練,這個管道對象就可以用於更平滑的部署。ColumnTransformer

在前面的例子中,我們以相同的方式對所有列進行插補和編碼。但是,我們經常需要對不同的列組應用不同的transformer。例如,我們希望將OneHotEncoder僅應用於分類列,而不應用於數值列。這就是ColumnTransformer的用武之地。

這一次,我們將對保留所有列的數據集進行分區,以便同時具有數值和類別特徵。

# 劃分數據X_train, X_test, y_train, y_test = train_test_split(df.drop(columns=['total_bill']), df['total_bill'], test_size=.2, random_state=seed)# 定義分類列categorical = list(X_train.select_dtypes('category').columns)print(f"Categorical columns are: {categorical}")# 定義數字列numerical = list(X_train.select_dtypes('number').columns)print(f"Numerical columns are: {numerical}")

我們根據數據類型將特徵分為兩組。列分組可以根據數據的適當情況進行。例如,如果不同的預處理管道更適合分類列,則可以將它們進一步拆分為多個組。

上一節的代碼現在將不再工作,因為我們有多個數據類型。讓我們看一個例子,其中我們使用ColumnTransformer和Pipeline在存在多個數據類型的情況下執行與之前相同的轉換。

# 定義分類管道cat_pipe = Pipeline([('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])# 使ColumnTransformer擬合訓練數據preprocessor = ColumnTransformer(transformers=[('cat', cat_pipe, categorical)], remainder='passthrough')preprocessor.fit(X_train)# 準備列名cat_columns = preprocessor.named_transformers_['cat']['encoder'].get_feature_names(categorical)columns = np.append(cat_columns, numerical)# 檢查訓練前後的數據print("******************** Training data ********************")display(X_train)display(pd.DataFrame(preprocessor.transform(X_train), columns=columns))# 檢查測試前後的數據print("******************** Test data ********************")display(X_test)display(pd.DataFrame(preprocessor.transform(X_test), columns=columns))

分類列的輸出與上一節的輸出相同。唯一的區別是這個版本有一個額外的列:size。我們已經將cat_pipe(在上一節中稱為pipe)傳遞給ColumnTransformer來轉換分類列,並指定remainment='passthrough'以保持其餘列不變。

讓我們用中值填充缺失值,並將其縮放到0和1之間:

# 定義分類管道cat_pipe = Pipeline([('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])# 定義數值管道num_pipe = Pipeline([('imputer', SimpleImputer(strategy='median')), ('scaler', MinMaxScaler())])# 使ColumnTransformer擬合訓練數據preprocessor = ColumnTransformer(transformers=[('cat', cat_pipe, categorical), ('num', num_pipe, numerical)])preprocessor.fit(X_train)# 準備列名cat_columns = preprocessor.named_transformers_['cat']['encoder'].get_feature_names(categorical)columns = np.append(cat_columns, numerical)# 檢查訓練前後的數據print("******************** Training data ********************")display(X_train)display(pd.DataFrame(preprocessor.transform(X_train), columns=columns))# 檢查測試前後的數據print("******************** Test data ********************")display(X_test)display(pd.DataFrame(preprocessor.transform(X_test), columns=columns))

現在所有列都被插補,範圍在0到1之間。使用ColumnTransformer和Pipeline,我們將數據分成兩組,將不同的管道和不同的Transformer應用到每組,然後將結果粘貼在一起:

儘管在我們的示例中,數值管道和分類管道中的步驟數相同,但管道中可以有任意數量的步驟,並且不同列子集的步驟數不必相同。現在我們將一個模型添加到我們的示例中:

# 定義分類管道cat_pipe = Pipeline([('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])# 定義數值管道num_pipe = Pipeline([('imputer', SimpleImputer(strategy='median')), ('scaler', MinMaxScaler())])# 組合分類管道和數值管道preprocessor = ColumnTransformer(transformers=[('cat', cat_pipe, categorical), ('num', num_pipe, numerical)])# 在管道上安裝transformer和訓練數據的estimatorpipe = Pipeline(steps=[('preprocessor', preprocessor), ('model', LinearRegression())])pipe.fit(X_train, y_train)# 預測訓練數據y_train_pred = pipe.predict(X_train)print(f"Predictions on training data: {y_train_pred}")# 預測測試數據y_test_pred = pipe.predict(X_test)print(f"Predictions on test data: {y_test_pred}")

為了將ColumnTransformer中指定的預處理步驟與模型結合起來,我們在外部使用了一個管道。以下是它的視覺表現:

當我們需要對不同的列子集執行不同的操作時,ColumnTransformer很好地補充了管道。

FeatureUnion

以下代碼的輸出在本節中被省略,因為它們與ColumnTransformer章節的輸出相同。

FeatureUnion是另一個有用的工具。它可以做ColumnTransformer剛剛做過的事情,但要做得更遠:

# 自定義管道class ColumnSelector(BaseEstimator, TransformerMixin): """Select only specified columns.""" def __init__(self, columns): self.columns = columns def fit(self, X, y=None): return self def transform(self, X): return X[self.columns]# 定義分類管道cat_pipe = Pipeline([('selector', ColumnSelector(categorical)), ('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])# 定義數值管道num_pipe = Pipeline([('selector', ColumnSelector(numerical)), ('imputer', SimpleImputer(strategy='median')), ('scaler', MinMaxScaler())])# FeatureUnion擬合訓練數據preprocessor = FeatureUnion(transformer_list=[('cat', cat_pipe), ('num', num_pipe)])preprocessor.fit(X_train)# 準備列名cat_columns = preprocessor.transformer_list[0][1][2].get_feature_names(categorical)columns = np.append(cat_columns, numerical)# 檢查訓練前後的數據print("******************** Training data ********************")display(X_train)display(pd.DataFrame(preprocessor.transform(X_train), columns=columns))# 檢查測試前後的數據print("******************** Test data ********************")display(X_test)display(pd.DataFrame(preprocessor.transform(X_test), columns=columns))我們可以將FeatureUnion視為創建數據的副本,並行地轉換這些副本,然後將結果粘貼在一起。這裡的術語副本更像是一種輔助概念化的類比,而不是實際採用的技術。

在每個管道的開始,我們添加了一個額外的步驟,在這裡我們使用一個定製的轉換器來選擇相關的列:第14行和第19行的ColumnSelector。下面是我們可視化上面的腳本的圖:

現在,是時候向腳本添加模型了:

# 定義分類管道cat_pipe = Pipeline([('selector', ColumnSelector(categorical)), ('imputer', SimpleImputer(strategy='constant', fill_value='missing')), ('encoder', OneHotEncoder(handle_unknown='ignore', sparse=False))])# 定義數值管道num_pipe = Pipeline([('selector', ColumnSelector(numerical)), ('imputer', SimpleImputer(strategy='median')), ('scaler', MinMaxScaler())])# 組合分類管道和數值管道preprocessor = FeatureUnion(transformer_list=[('cat', cat_pipe), ('num', num_pipe)])# 組合分類管道和數值管道pipe = Pipeline(steps=[('preprocessor', preprocessor), ('model', LinearRegression())])pipe.fit(X_train, y_train)# 預測訓練數據y_train_pred = pipe.predict(X_train)print(f"Predictions on training data: {y_train_pred}")# 預測測試數據y_test_pred = pipe.predict(X_test)print(f"Predictions on test data: {y_test_pred}")它看起來很像我們用ColumnTransformer做的。

如本例所示,使用FeatureUnion比使用ColumnTransformer要複雜得多。因此,在我看來,在類似的情況下最好使用ColumnTransformer。

然而,FeatureUnion肯定有它的位置。如果你需要以不同的方式轉換相同的輸入數據並將它們用作特徵,FeatureUnion就是其中之一。例如,如果你正在處理一個文本數據,並且希望對數據進行tf-idf矢量化以及提取文本長度,FeatureUnion是一個完美的工具。

總結

你可能已經注意到,Pipeline是超級明星。ColumnTransformer和FeatureUnion是用於管道的附加工具。ColumnTransformer更適合於並行劃分,而FeatureUnion允許我們在同一個輸入數據上並行應用多個轉換器。下面是一個簡單的總結:

相關焦點

  • China, Russia launch gas pipeline
    The two presidents, Xi in Beijing and Putin in Sochi, Russia, greeted each other, and Xi expressed gratitude toward the workers building the pipeline.
  • Transformer在CV領域有可能替代CNN嗎?
    目前來看,transformer在處理這些要素之間的關係上更自然也更有效。 從這兩方面的角度來看,將CNN在處理底層視覺上的優勢和transformer在處理視覺要素和物體之間關係上的優勢相結合,應該是一個非常有希望的方向。
  • 深2.5至4倍,參數和計算量卻更少,DeLighT Transformer是怎麼做到的?
    總的來說,DeLighT 的網絡深度是標準 transformer 模型的 2.5 到 4 倍,但參數量和計算量都更少。DeLighT 的核心是 DExTra 變換(DExTra transformation),該變換使用組線性變換和擴展 - 縮小(expand-reduce)策略來有效地改變 DeLighT 塊的寬度和深度。
  • 解析Transformer模型
    轉自 | GiantPandaCV作者 | zzk【導讀】GiantPandaCV導語:這篇文章為大家介紹了一下Transformer模型,Transformer模型原本是NLP中的一個Idea,後來也被引入到計算機視覺中,例如前面介紹過的DETR就是將目標檢測算法和Transformer進行結合,另外基於
  • 如何使用pipeline function獲得實時輸出
    如果要在pipeline中執行DML操作,則必須使用自治事務,否則會報ORA-14551錯誤 create or replace function f_pipeline_testdmlreturn MsgTypePIPELINEDasbegin for i in 1 .. 10 loop insert into test
  • Facebook AI的DETR,一種基於Transformer的目標檢測方法
    它在單個實例中獲取整個圖像,並預測這些框的邊界框坐標和類概率。utm_source=blog&utm_medium=facebook-detection-transformer-detr-a-transformer-based-object-detection-approach使用流行的YOLO框架進行目標檢測的實用指南https://www.analyticsvidhya.com/blog/2018/12/practical-guide-object-detection-yolo-framewor-python
  • 陳丹琦新作:關係抽取新SOTApipeline挫敗joint
    近期研究多採用 joint 方式建模兩個子任務,而陳丹琦等人新研究提出一種簡單高效的 pipeline 方法,在多個基準上獲得了新的 SOTA 結果。這個問題可以分解為兩個子任務:命名實體識別和關係抽取。早期研究採用 pipeline 方法:訓練一個模型來抽取實體,另一個模型對實體之間的關係進行分類。
  • Transformer在CV領域有可能替代CNN嗎?|卷積|神經網絡|算子|上下文...
    目前來看,transformer在處理這些要素之間的關係上更自然也更有效。  從這兩方面的角度來看,將CNN在處理底層視覺上的優勢和transformer在處理視覺要素和物體之間關係上的優勢相結合,應該是一個非常有希望的方向。
  • 熱門的模型跨界,Transformer、GPT做CV任務一文大盤點
    該研究推出的 Transformer 視覺版本——Detection Transformer(以下簡稱 DETR),可用於目標檢測和全景分割。與之前的目標檢測系統相比,DETR 的架構有了根本上的改變,也是第一個將 Transformer 成功整合為檢測 pipeline 中心構建塊的目標檢測框架。
  • 將Docker與pipeline一起使用
    Jenkinsfile(聲明性管道)pipeline { agent { docker { image 'node:7-alpine' } } stages { stage('Test') { steps { sh 'node --version' } } }}切換腳本管道 (高級)當管道執行時,Jenkins將自動啟動指定的容器並在其中執行定義的步驟:
  • Transformer生成論文摘要方法已出
    要將帶有邏輯結構的長文本進行抽取和摘要化處理,這樣的算法並不常見。現在,這篇介紹論文摘要抽取生成的論文,也許會給這個 NLP 中的經典任務帶來新的解決思路。比如,通過在測試時提供相似格式的數據,可讓模型學會解決摘要任務;即語言模型可以基於文檔的引言和之後更長的正文生成一個摘要。具體而言,論文的研究者使用了單個類 GPT 的 transformer 語言模型,並在文檔及其摘要上進行訓練。在推理階段,語言會基於輸入文檔執行生成任務(見圖 1)。研究者將這個任務劃分為了兩個步驟:抽取和摘要。
  • 陳丹琦新作:關係抽取新SOTA,用pipeline方式挫敗joint模型
    機器之心報導編輯:魔王、小舟端到端關係抽取涉及兩個子任務:命名實體識別和關係抽取。近期研究多採用 joint 方式建模兩個子任務,而陳丹琦等人新研究提出一種簡單高效的 pipeline 方法,在多個基準上獲得了新的 SOTA 結果。
  • 英語詞根 變形金剛Transformer
    英文:「Are you ok ho
  • FATE1.0重磅發布:首個可視化聯邦學習產品與聯邦pipeline生產服務...
    作為全球首個聯邦學習工業級技術框架,FATE支持聯邦學習架構體系與各種機器學習算法的安全計算,實現了基於同態加密和多方計算(MPC)的安全計算協議,能夠幫助多個組織機構在符合數據安全和政府法規前提下,有效和協作地進行數據使用和聯合建模。
  • Unroll & Pipeline | 細粒度並行優化的完美循環
    HLS 優化設計的最關鍵指令有兩個:一個是流水線 (pipeline) 指令,一個是數據流(dataflow) 指令。正確地使用好這兩個指令能夠增強算法地並行性,提升吞吐量,降低延遲但是需要遵循一定的代碼風格。
  • 【TensorFlow2.0】Transformer聊天機器人教程
    角色和電影信息可分別在movie_characters_metadata.txt和movie_titles_metadata.txt中找到。使用TensorFlow數據集SubwordTextEncoder構建標記生成器(將文本映射到ID和ID到文本)。對每個句子進行標記並添加START_TOKEN和END_TOKEN以指示每個句子的開頭和結尾。過濾掉包含超過MAX_LENGTH 個令牌的句子。
  • 這六大方法如何讓Transformer輕鬆應對高難度長文本序列?
    可以想像的到,稀疏 transformer 之所以起作用,部分原因是它學到的的注意力模式與實際學習的密集注意力模式並沒有什麼不同。在 Kevin Clark 等人發表的文章「What Does BERT Look At?
  • Pipeline治療加不加圈?懂行的人這麼說!(上)
    Use of coils in conjunction with the pipeline embolization device for treatment of intracranial aneurysms. Neurosurgery 2015;76:142–9.
  • GATK pipeline鑑定基因組變異的scripts
    前面我已經和大家分享了我的GATK-pipeline,最近GATK更新到了4.0,於是這裡有一些更新,我把我更新的scripts和大家分享。