您現在的位置是:首頁 > 農業

如何用不同長度的觀測資料對資料集進行分類

由 不靠譜的貓 發表于 農業2021-12-19
簡介第一個顯示最大深度等於無時的分類器精度,這意味著樹深度沒有嚴格約束,但取決於分割質量:如圖所示,當僅使用兩個clusters訓練資料時,分類器實現最佳準確度值

可測集的子集是可測集嗎

如何用不同長度的觀測資料對資料集進行分類

“經典”機器學習演算法通常要求訓練資料集採用兩個矩陣的格式——一個帶樣本的矩陣和一個帶目標的陣列。然而,如何處理觀測沒有固定長度的資料集呢?

考慮以下情況。您有一個檔案資料集,其中每個檔案包含一個觀察,並且您事先不知道每個檔案的長度。不可能將每個檔案“flatten”為數字向量,並將這些向量提供給訓練演算法,因為它們的長度不匹配。我們如何“normalize”觀測,使我們能夠將它們連線到矩陣中?

在這篇文章中,我將討論Wrist-Worn Accelerometer資料集(https://archive。ics。uci。edu/ml/datasets/Dataset+for+ADL+Recognition+with+Wrist-worn+Accelerometer),它具有各種長度觀測的特徵。我將展示如何應用K-Means聚類和隨機森林分類器將資料集分類為具有固定長度特徵向量的任何其他資料集。

資料集概述

Wrist-Worn Accelerometer資料集代表一組具有3維加速度計測量值的檔案。每個檔案屬於14個類中的一個。每個類代表由加速度計腕帶的佩戴者執行的單個動作,例如,步行,飲酒,進食等。總共有839個觀測值。

以下直方圖顯示,通常,每個資料集的檔案具有不同數量的記錄。例如,我們有26個檔案,270個測量值,6個檔案,417個記錄,依此類推。直方圖包括並不代表所有可能的長度,即,存在具有不同觀察數量的檔案。

如何用不同長度的觀測資料對資料集進行分類

我們的目標是將這一堆檔案轉換為數字矩陣和目標陣列,我們可以將其傳遞到機器學習分類器中。

k - means聚類

為了直觀瞭解K-Means演算法如何幫助我們將資料集的觀測資料規範化,讓我們考慮以下圖片:

如何用不同長度的觀測資料對資料集進行分類

該圖顯示了從這個 scikit-learn documentation snippet中獲取的手寫數字資料的聚類過程的結果。藍色的點表示資料集的投影到二維空間中使用PCA演算法,紅色的點在聚類過程中被推斷出來。

從影象上可以看出,聚類過程可以被看作是一種原始的連續的連續的平面,形成了一層不均勻的多邊形,而這些多邊形的數量是由我們指定的,而我們選擇的是k -均值演算法。

如果我們用最近的(關於歐幾里得距離)紅點替換每個藍點,我們就得到了原始點陣列的離散表示。因此,不管我們的觀測結果如何,它們都被固定大小的質心陣列所取代。

現在情況變得更加清楚了。我們有一個檔案(通常)有任意數量的帶有觀察的行。然後,我們將聚類過程應用到這些觀測中。K-Means演算法返回一個簇質心陣列。我們將這些陣列連線到一個固定大小的向量中。從原理上講,可以用下圖來表示該過程:

如何用不同長度的觀測資料對資料集進行分類

將K-Means聚類應用於單個數據集的檔案

當我們完成每個檔案的過程時,我們將擁有一個具有固定行數和列數的常用數值資料集,並且可以應用任何合適的機器學習演算法。

決策樹和隨機森林

決策樹是表示為分析資料集的遞迴分割槽的分類器。從數學上講,它表示一個函式,該函式將屬性值的向量作為輸入,並返回一個“決策”——一個輸出值。樹透過對給定的觀察屬性執行一系列測試來做出決定。樹中的每個內部節點都對應於對觀察值的一個屬性的值的測試,節點的分支被標記為屬性的可能值。樹中的每個葉節點都指定一個函式要返回的值。

如何用不同長度的觀測資料對資料集進行分類

在Wine資料集上訓練的決策樹的示例

決策樹是非常直觀的資料分類技術,它“詢問”資料集觀察組的是 - 否問題,並根據這些“答案”做出決定。但是,這種技術存在一個缺點:決策樹對您正在訓練它們的資料非常敏感。採用原始資料集的各種訓練子集,您可能會獲得不同的樹,並且它們的準確性可能會有很大差異。此外,單個樹預測精度通常不會太高。

如何緩解這些問題呢?其中一個可能的解決方案是構建幾棵樹並平均預測。這個結果的理論基礎來自關於弱學習者的定理。這個想法是,如果你有一個 比一個隨機的分類器更好的分類器(比如,在執行二進位制分類任務時比均勻硬幣要好一點),那麼擁有足夠數量的這種弱分類器將能夠預測類幾乎完美準確。

嚴格地說,為了應用該定理,資料集樣本應該是獨立且相同分佈的隨機變數的要求,並且樣本來自的分佈不應該變化很大。然而,如果每個分類器的訓練過程與其他分類器的訓練過程略有不同,這種方法在實踐中的效果相當好。

將一組弱學習者加入單個強學習者的想法可以應用於任何分類器,而不僅僅應用於決策樹。在將這種方法應用於一組樹時,強學習者稱為隨機森林。

現在,當資料集準備就緒並且選擇了分類器時,是時候將所有內容連線在一起並構建資料分類管道。

The Pipeline

在使用優秀的scikit-learn庫時,構建應該在單個機器上執行的資料分類管道是一個非常簡單的過程。唯一需要注意的是我們資料集的特定屬性,需要應用預處理技術。

因此,我們只需要讀取資料集檔案並在庫的類之上建立幾個thin wrappers。下面的Python程式碼段顯示瞭如何實現這一點:

“”“

Wrist-Worn Accelerometer Dataset using scikit-learn。

”“”

import re

from os import listdir

from os。path import exists, join, basename, isdir

import numpy as np

from sklearn。pipeline import make_pipeline

from sklearn。base import TransformerMixin, clone

from sklearn。preprocessing import LabelEncoder, StandardScaler

from sklearn。cluster import KMeans

from sklearn。ensemble import RandomForestClassifier

from sklearn。model_selection import train_test_split

class AccelerometerDatasetReader:

“”“

A class intended to read the accelerometer dataset and parse their names

to extract observations classes。

”“”

FILE_REGEX = re。compile(‘^Accelerometer-([\d-]+)-(\w+)-(\w\d+)。txt$’)

def __init__(self, ignore_model=True):

self。ignore_model = ignore_model

self。samples_ = None

self。targets_ = None

self。encoder_ = None

def read(self, root):

if not exists(root):

raise ValueError(f‘path does not exist: {root}’)

samples, targets = [], []

for folder in listdir(root):

path = join(root, folder)

if not isdir(path):

continue

is_model = folder。lower()。endswith(‘model’)

if self。ignore_model and is_model:

continue

for filename in listdir(path):

match = self。FILE_REGEX。match(basename(filename))

if match is None:

continue

filepath = join(path, filename)

_, category, _ = match。groups()

with open(filepath) as lines:

points = [[

int(value) for value in line。split()]

for line in lines]

samples。append(points)

targets。append(category)

encoder = LabelEncoder()

self。samples_ = samples

self。targets_ = encoder。fit_transform(targets)

self。encoder_ = encoder

@property

def dataset(self):

return self。samples_, self。targets_

class BatchTransformer(BaseEstimator, TransformerMixin):

“”“

In our case, each observation is not a 1-dimensional array, but

a matrix, i。e。, an array of arrays。 Therefore, we can‘t apply

transformers to it, but we can iterate through matrix rows and

apply a scikit-learn transformer to each of them separately。

”“”

def __init__(self, base_transformer):

self。base_transformer = base_transformer

def fit(self, X, y=None):

return self

def transform(self, batch, y=None):

if y is not None:

raise ValueError(

’cannot apply batch transformer in supervised fashion‘)

transformed = []

for record in batch:

transformer = clone(self。base_transformer)

transformed。append(transformer。fit_transform(record))

return transformed

class KMeansQuantization(BaseEstimator, TransformerMixin):

“”“

As in the previous case, we can’t apply K-Means directly to the

dataset but need to iterate through samples and concatenate

inferred cluster centroids into 1-dimensional arrays, and then

to concatenate these arrays into a matrix。

”“”

def __init__(self, k=5):

self。k = k

def fit(self, X, y=None):

return self

def transform(self, X):

features = []

for record in X:

kmeans = KMeans(n_clusters=self。k)

kmeans。fit_transform(record)

feature_vector = kmeans。cluster_centers_。flatten()

features。append(feature_vector)

return np。array(features)

def main():

root = join(‘datasets’, ‘adl’)

reader = AccelerometerDatasetReader()

reader。read(root)

X, y = reader。dataset

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y)

pipeline = make_pipeline(

BatchTransformer(StandardScaler()),

KMeansQuantization(k=3),

RandomForestClassifier(n_estimators=1000))

pipeline。fit(X_train, y_train)

y_preds = pipeline。predict(X_test)

acc = np。mean(y_preds == y_test)

print(f‘Dataset accuracy: {acc:2。2%}’)

if __name__ == ‘__main__’:

main()

這段程式碼非常簡單明瞭。AccelerometerDatasetReader是解析資料集檔案並建立兩個帶有觀察和目標的容器的類。BatchTransformer類是一個wrapper ,它將scikit-learn transformer應用於每個資料集檔案。最後,KMeansQuantization是KMeans估計器上的一個wrapper ,將質心連線成固定大小的特徵。

所有這些類構成一個單獨的資料分類管道,它接受一個訓練資料集,並在一個測試子集上執行預測。我們所獲得的測試精度等於58。10%,儘管這個值可以根據所選資料集的分割而變化。

效能最佳化

我們是否可以使用不同的估計器引數來獲得更好的效能,或者找到一個精度相同的更小的模型?為了得到這個問題的答案,我們應該使用交叉驗證和grid search。

其思想是選擇幾個引數的組合,訓練分類器,然後比較它們的效能。然後我們只需要選擇一個最準確的分數。

在我們的例子中,我們有(至少)三個可能會影響分類器效能的引數:

量化資料時的clusters數量

集合中的樹數。

最大允許樹的深度

下面的Python程式碼片段展示瞭如何結合grid search和scikit-learn pipeline,找到引數的最佳組合:

“”“

Main repository: https://github。com/devforfu/Blog/tree/master/trees

”“”

import os

from os。path import join

from pprint import pprint

import pandas as pd

from sklearn。pipeline import make_pipeline

from sklearn。preprocessing import StandardScaler

from sklearn。model_selection import GridSearchCV

from sklearn。ensemble import RandomForestClassifier

# imports from local file sitting in working directory

from scikit_learn import AccelerometerDatasetReader

from scikit_learn import BatchTransformer

from scikit_learn import KMeansQuantization

def main():

root = join(‘datasets’, ‘adl’)

reader = AccelerometerDatasetReader()

reader。read(root)

X, y = reader。dataset

pipeline = make_pipeline(

BatchTransformer(StandardScaler()),

KMeansQuantization(),

RandomForestClassifier())

#define a dictionary that we’re going to use to search for the best classifier

params_grid = {

‘kmeansquantization__k’: [2, 3, 4],

‘randomforestclassifier__n_estimators’: [10, 100, 250, 500],

‘randomforestclassifier__max_depth’: [1, 10, None]}

search = GridSearchCV(pipeline, params_grid, scoring=‘accuracy’, n_jobs=-1, verbose=2)

search。fit(X, y)

cv_results = search。cv_results_

params = pd。DataFrame(cv_results[‘params’])。rename(columns={

‘kmeansquantization__k’: ‘k’,

‘randomforestclassifier__max_depth’: ‘max_depth’,

‘randomforestclassifier__n_estimators’: ‘n_estimators’

})

params[‘mean_train_score’] = cv_results[‘mean_train_score’]

params[‘mean_test_score’] = cv_results[‘mean_test_score’]

best = params。iloc[search。best_index_]

print(‘Best classifier parameters:’)

pprint(best。to_dict())

if __name__ == ‘__main__’:

main()

看看第31-34行。這些行定義了一個字典,我們將用它來搜尋最佳分類器。字典鍵是指變換器和分類器引數,字首為低位類名,後跟兩個下劃線。這些值定義了引數搜尋域:我們將嘗試所有可能的組合,即列表的笛卡爾積。

為了更好地理解引數如何影響分類精度,讓我們建立幾個解釋圖。第一個顯示最大深度等於無時的分類器精度,這意味著樹深度沒有嚴格約束,但取決於分割質量:

如何用不同長度的觀測資料對資料集進行分類

如圖所示,當僅使用兩個clusters訓練資料時,分類器實現最佳準確度值。此外,增加集合中的樹數通常會提高其效能。

下一個圖顯示了我們明確限制樹深度時的準確度:

如何用不同長度的觀測資料對資料集進行分類

這一次分類器的效能越來越差。似乎設定特定的深度限制會阻礙訓練過程找到更優的解決方案。

最後,如果我們選擇一個極端的情況,嘗試用決策殘差對資料集進行分類,即最大深度等於1的決策樹

如何用不同長度的觀測資料對資料集進行分類

上面的圖表顯示在我們的案例中它不是一個好主意。該模型不夠複雜,無法捕獲屬性及其類之間的所有隱藏依賴關係,並且執行效果不佳。

因此,我們可以得出結論,當選擇較少數量的聚類和足夠數量的決策樹時,我們可以獲得更高的準確度。

結論

決策樹及其集合是直觀清晰且功能強大的機器學習技術。原始演算法和一系列優秀的庫有許多改進,它們允許以並行方式訓練樹集合並獲得更高的準確性(例如XGBoost),即使是簡單的實現也能顯示出不錯的結果,並證明它是機器學習工程師工具箱中必不可少的演算法。

推薦文章