主頁 >  其他 > AI 音辨世界:藝術小白的我,靠這個AI模型,速識音樂流派選擇音樂

AI 音辨世界:藝術小白的我,靠這個AI模型,速識音樂流派選擇音樂

2022-10-27 06:48:14 其他

?? 作者:韓信子@ShowMeAI
?? 資料分析實戰系列:https://www.showmeai.tech/tutorials/40
?? 機器學習實戰系列:https://www.showmeai.tech/tutorials/41
?? 本文地址:https://www.showmeai.tech/article-detail/309
?? 宣告:著作權所有,轉載請聯系平臺與作者并注明出處
?? 收藏ShowMeAI查看更多精彩內容

只要給到足夠的相關資訊,AI模型可以迅速學習一個新的領域問題,并構建起很好的知識和預估系統,比如音樂領域,借助于歌曲相關資訊,模型可以根據歌曲的音頻和歌詞特征將歌曲精準進行流派分類,在本篇內容中 ShowMeAI 就帶大家一起來看看,如何基于機器學習完成對音樂的識別分類,

本篇內容使用到的資料集為 ??Spotify音樂資料集,大家也可以通過 ShowMeAI 的百度網盤地址快速下載,

?? 實戰資料集下載(百度網盤):公眾號『ShowMeAI研究中心』回復『實戰』,或者點擊 這里 獲取本文 [18]音樂流派識別的機器學習系統搭建與調優 『Spotify 音樂資料集

? ShowMeAI官方GitHub:https://github.com/ShowMeAI-Hub

我們在本篇內容中將用到最常用的 boosting 集成工具庫 LightGBM,并且將結合 optuna 工具庫對其進行超引數調優,優化模型效果,

關于 LightGBM 的模型原理和使用詳細講解,歡迎大家查閱 ShowMeAI 的文章:

??圖解機器學習演算法(11) | LightGBM模型詳解

??機器學習實戰(5) | LightGBM建模應用詳解

本篇文章包含以下內容板塊:

  • 資料概覽和預處理
  • EDA探索性資料分析
  • 歌詞特征&資料降維
  • 建模和超引數優化
  • 總結&經驗

?? 資料概覽和預處理

本次使用的資料集包含超過 18000 首歌曲的資訊,包括其音頻特征資訊(如活力度,播放速度或調性等),以及歌曲的歌詞,

我們讀取資料并做一個速覽如下:

import pandas as pd
# 讀取資料
data = https://www.cnblogs.com/showmeai/p/pd.read_csv("spotify_songs.csv")
# 資料速覽
data.head()
# 資料基本資訊
data.info()

欄位說明如下:

欄位 含義
track_id 歌曲唯一ID
track_name 歌曲名稱
track_artist 歌手
lyrics 歌詞
track_popularity 唱片熱度
track_album_id 唱片的唯一ID
track_album_name 唱片名字
track_album_release_date 唱片發行日期
playlist_name 歌單名稱
playlist_id 歌單ID
playlist_genre 歌單風格
playlist_subgenre 歌單子風格
danceability 舞蹈性描述的是根據音樂元素的組合,包括速度、節奏的穩定性、節拍的強度和整體的規律性,來衡量一首曲目是否適合跳舞,0.0的值是最不適合跳舞的,1.0是最適合跳舞的,
energy 能量是一個從0.0到1.0的度量,代表強度和活動的感知度,一般來說,有能量的曲目給人的感覺是快速、響亮,例如,死亡金屬有很高的能量,而巴赫的前奏曲在該量表中得分較低,
key 音軌的估測總調,用標準的音階符號將整數映射為音高,例如,0=C,1=C?/D?,2=D,以此類推,如果沒有檢測到音調,則數值為-1,
loudness 軌道的整體響度,單位是分貝(dB),響度值是整個音軌的平均值,對于比較音軌的相對響度非常有用,
mode 模式表示音軌的調式(大調或小調),即其旋律內容所來自的音階型別,大呼叫1表示,小呼叫0表示,
speechiness 言語性檢測音軌中是否有口語,錄音越是完全類似于語音(如脫口秀、說唱、詩歌),屬性值就越接近1.0,
acousticness 衡量音軌是否為聲學的信心指數,從0.0到1.0,1.0表示該曲目為原聲的高置信度,
instrumentalness 預測一個音軌是否包含人聲,越接近1.0該曲目就越有可能不包含人聲內容,
liveness 檢測錄音中是否有聽眾存在,越接近現場演出數值越大,
valence 0.0到1.0,描述了一個音軌所傳達的音樂積極性,接近1的曲目聽起來更積極(如快樂、歡快、興奮),而接近0的曲目聽起來更消極(如悲傷、壓抑、憤怒),
tempo 軌道的整體估計速度,單位是每分鐘節拍(BPM),
duration_ms 歌曲的持續時間(毫秒)
language 歌詞的語言語種

原始的資料有點雜亂,我們先進行過濾和資料清洗,

# 資料工具庫
import pandas as pd
import re

# 歌詞處理的nlp工具庫
import nltk
from nltk.corpus import stopwords
from collections import Counter
# nltk.download('stopwords')

# 讀取資料
data = https://www.cnblogs.com/showmeai/p/pd.read_csv("spotify_songs.csv")
# 欄位選擇
keep_cols = [x for x in data.columns if not x.startswith("track") and not x.startswith("playlist")]
keep_cols.append("playlist_genre")
df = data[keep_cols].copy()
# 只保留英文歌曲
subdf = df[(df.language == "en") & (df.playlist_genre != "latin")].copy().drop(columns = "language")


# 歌詞規整化,全部小寫
pattern = r"[^a-zA-Z ]"
subdf.lyrics = subdf.lyrics.apply(lambda x: re.sub(pattern, "", x.lower()))

# 移除停用詞
subdf.lyrics = subdf.lyrics.apply(lambda x: ' '.join([word for word in x.split() if word not in (stopwords.words("english"))]))

# 查看歌詞中的詞匯出現的頻次

# 連接所有歌詞
all_text = " ".join(subdf.lyrics)
# 統計詞頻
word_count = Counter(all_text.split())
# 如果一個詞在200首以上的歌里都出現,則保留,否則視作低頻過濾掉
keep_words = [k for k, v in word_count.items() if v > 200]
# 構建一個副本
lyricdf = subdf.copy().reset_index(drop=True)
# 欄位名稱規范化
lyricdf.columns = ["audio_"+ x if not x in ["lyrics", "playlist_genre"] else x for x in lyricdf.columns]
# 歌詞內容
lyricdf.lyrics = lyricdf.lyrics.apply(lambda x: Counter([word for word in x.split() if word in keep_words]))
# 構建詞匯詞頻Dataframe
unpacked_lyrics = pd.DataFrame.from_records(lyricdf.lyrics).add_prefix("lyrics_")
# 缺失填充為0
unpacked_lyrics = unpacked_lyrics.fillna(0) 
# 拼接并洗掉原始歌詞列
lyricdf = pd.concat([lyricdf, unpacked_lyrics], axis = 1).drop(columns = "lyrics")
# 排序
reordered_cols = [col for col in lyricdf.columns if not col.startswith("lyrics_")] + sorted([col for col in lyricdf.columns if col.startswith("lyrics_")])
lyricdf = lyricdf[reordered_cols]

# 存盤為新的csv檔案
lyricdf.to_csv("music_data.csv", index = False)

主要的資料預處理在上述代碼的注釋里大家可以看到,核心步驟概述如下:

  • 過濾資料以僅包含英語歌曲并洗掉“拉丁”型別的歌曲(因為這些歌曲幾乎完全是西班牙語,所以會產生嚴重的類不平衡),
  • 通過將歌詞設為小寫、洗掉標點符號和停用詞來整理歌詞,計算每個剩余單詞在歌曲歌詞中出現的次數,然后過濾掉所有歌曲中出現頻率最低的單詞(混亂的資料/噪音),
  • 清理與排序,

?? EDA探索性資料分析

和過往所有的專案一樣,我們也需要先對資料做一些分析和更進一步的理解,也就是EDA探索性資料分析程序,

EDA資料分析部分涉及的工具庫,大家可以參考ShowMeAI制作的工具庫速查表和教程進行學習和快速使用,
??資料科學工具庫速查表 | Pandas 速查表
??圖解資料分析:從入門到精通系列教程

首先我們檢查一下我們的標簽(流派)的類分布和平衡

# 分組統計
by_genre = data.groupby("playlist_genre")["audio_key"].count().reset_index()
fig, ax = plt.subplots()

# 繪圖
ax.bar(by_genre.playlist_genre, by_genre.audio_key)
ax.set_ylabel("Number of Observations")
ax.set_xlabel("Genre")
ax.set_title("Observations per Class")
ax.set_ylim(0, 4000)

# 每個柱子上標注數量
rects = ax.patches
for rect in rects:
    height = rect.get_height()
    ax.text(
        rect.get_x() + rect.get_width() / 2, height + 5, height, ha="center", va="bottom"
    )

存在輕微的類別不平衡,那后續我們在交叉驗證和訓練測驗拆分時候注意資料分層(保持比例分布) 即可,

# 把所有欄位切分為音頻和歌詞列
audio = data[[x for x in data.columns if x.startswith("audio")]]
lyric = data[[x for x in data.columns if x.startswith("lyric")]]
# 讓欄位命名更簡單一些
audio.columns = audio.columns.str.replace("audio_", "")
lyric.columns = lyric.columns.str.replace("lyric_", "")

?? 歌詞特征&資料降維

我們的機器學習演算法在處理高維資料的時候,可能會有一些性能問題,有時候我們會對資料進行降維處理,

降維的本質是將高維資料投影到低維子空間中,同時盡可能多地保留資料中的資訊,關于降維大家可以查看 ShowMeAI 的演算法原理講解文章 ??圖解機器學習 | 降維演算法詳解

我們探索一下降維演算法(PCA 和 t-SNE)在我們的歌詞資料上降維是否合適,并做一點調整,

?? PCA主成分分析

PCA是最常用的降維演算法之一,我們借助這個演算法可以對資料進行降維,并且看到它保留大概多少的原始資訊量,例如,在我們當前場景中,如果將歌詞減少到400 維,我們仍然保留了歌詞中60% 的資訊(方差) ;如果降維到800維,則可以覆寫 80% 的原始資訊(方差),歌詞本身是很稀疏的,我們對其降維也能讓模型更好地建模,

# 常規資料工具庫
import pandas as pd
import numpy as np
# 繪圖
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
# 資料處理
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA

# 讀取資料
data = https://www.cnblogs.com/showmeai/p/pd.read_csv("music_data.csv")
# 切分為音頻與歌詞
audio = data[[x for x in data.columns if x.startswith("audio")]]
lyric = data[[x for x in data.columns if x.startswith("lyric")]]
# 特征欄位
y = data.playlist_genre

# 資料幅度縮放 + PCA降維
scaler = MinMaxScaler()
audio_features = scaler.fit_transform(audio)
lyric_features = scaler.fit_transform(lyric)

pca = PCA()
lyric_pca  = pca.fit_transform(lyric_features)
var_explained_ratio = pca.explained_variance_ratio_
   
# Plot graph
fig, ax = plt.subplots()
# Reduce margins
plt.margins(x=0.01)
# Get cumuluative sum of variance explained
cum_var_explained = np.cumsum(var_explained_ratio)
# Plot cumulative sum
ax.fill_between(range(len(cum_var_explained)), cum_var_explained,
                alpha = 0.4, color = "tab:orange",
                label = "Cum. Var.")
ax.set_ylim(0, 1)
# Plot actual proportions
ax2 = ax.twinx()
ax2.plot(range(len(var_explained_ratio)), var_explained_ratio,
         alpha = 1, color = "tab:blue", lw  = 4, ls = "--",
         label = "Var per PC")
ax2.set_ylim(0, 0.005)

# Add lines to indicate where good values of components may be
ax.hlines(0.6, 0, var_explained_ratio.shape[0], color = "tab:green", lw = 3, alpha = 0.6, ls=":")
ax.hlines(0.8, 0, var_explained_ratio.shape[0], color = "tab:green", lw = 3, alpha = 0.6, ls=":")
# Plot both legends together
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()
ax2.legend(lines + lines2, labels + labels2)
# Format axis as percentages
ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))
ax2.yaxis.set_major_formatter(mtick.PercentFormatter(1)) 

# Add titles and labels
ax.set_ylabel("Cum. Prop. of Variance Explained")
ax2.set_ylabel("Prop. of Variance Explained per PC", rotation = 270, labelpad=30)
ax.set_title("Variance Explained by Number of Principal Components")
ax.set_xlabel("Number of Principal Components")

?? t-SNE可視化

我們還可以更進一步,可視化資料在一系列降維程序中的可分離性,t-SNE演算法是一個非常有效的非線性降維可視化方法,借助于它,我們可以把資料繪制在二維平面觀察其分散程度,下面的t-SNE可視化展示了當我們使用所有1806個特征或將其減少為 1000、500、100 個主成分時,如果將歌詞資料投影到二維空間中會是什么樣子,

代碼如下:

from sklearn.manifold import TSNE
import seaborn as sns

# Merge numeric labels with normalised audio data and lyric principal components
tsne_processed = pd.concat([
    pd.Series(y, name = "genre"),
    pd.DataFrame(audio_features, columns=audio.columns),
    # Add prefix to make selecting pcs easier later on
    pd.DataFrame(lyric_pca).add_prefix("lyrics_pc_")
          ], axis = 1)

# Get t-SNE values for a range of principal component cutoffs, 1806 is all PCs
all_tsne = pd.DataFrame()
for cutoff in ["1806", "1000", "500", "100"]:
    # Create t-SNE object
    tsne = TSNE(init = "random", learning_rate = "auto")
    # Fit on normalised features (excluding the y/label column)
    tsne_results = tsne.fit_transform(tsne_processed.loc[:, "audio_danceability":f"lyrics_pc_{cutoff}"])
    
    # neater graph
    if cutoff == "1806":
        cutoff = "All 1806"
    # Get results
    tsne_df = pd.DataFrame({"y":y,
                        "tsne-2d-one":tsne_results[:,0],
                       "tsne-2d-two":tsne_results[:,1],
                           "Cutoff":cutoff})
    # Store results
    all_tsne = pd.concat([all_tsne, tsne_df], axis = 0)
    
# Plot gridplot
g = sns.FacetGrid(all_tsne, col="Cutoff", hue = "y",
                col_wrap = 2, height = 6,
                palette=sns.color_palette("hls", 4),
               )
# Add plots
g.map(sns.scatterplot, "tsne-2d-one", "tsne-2d-two", alpha = 0.3)
# Add titles/legends
g.fig.suptitle("t-SNE Plots vs Number of Principal Components Included", y = 1)
g.add_legend()

理想情況下,我們希望看到的是,在降維到某些主成分數量(例如 cutoff = 1000)時,流派變得更加可分離,

然而,上述 t-SNE 圖的結果顯示,PCA 這一步不同數量的主成分并沒有哪個會讓資料標簽更可分離,

?? 自編碼器降維

實際上我們有不同的方式可以完成資料降維任務,在下面的代碼中,我們提供了 PCA、截斷 SVD 和 Keras 自編碼器三種方式作為候選,調整配置即可進行選擇,

為簡潔起見,自動編碼器的代碼已被省略,但可以在 autoencode 內的功能 custom_functions.py 中的檔案庫,

# 通用庫
import pandas as pd
import numpy as np
# 建模庫
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA, TruncatedSVD
from sklearn.preprocessing import LabelEncoder, MinMaxScaler
# 神經網路
from keras.layers import Dense, Input, LeakyReLU, BatchNormalization
from keras.callbacks import EarlyStopping
from keras import Model

# 定義自編碼器
def autoencode(lyric_tr, n_components):
    """Build, compile and fit an autoencoder for
    lyric data using Keras. Uses a batch normalised,
    undercomplete encoder with leaky ReLU activations.
    It will take a while to train.
    --------------------------------------------------
    lyric_tr = df of lyric training data
    n_components = int, number of output dimensions
    from encoder
    """
    n_inputs = lyric_tr.shape[1]
    # 定義encoder
    visible = Input(shape=(n_inputs,))

    # encoder模塊1
    e = Dense(n_inputs*2)(visible)
    e = BatchNormalization()(e)
    e = LeakyReLU()(e)
    # encoder模塊2
    e = Dense(n_inputs)(e) 
    e = BatchNormalization()(e)
    e = LeakyReLU()(e)
    bottleneck = Dense(n_components)(e)

    # decoder模塊1
    d = Dense(n_inputs)(bottleneck)
    d = BatchNormalization()(d)
    d = LeakyReLU()(d)
    # decoder模塊2
    d = Dense(n_inputs*2)(d)
    d = BatchNormalization()(d)
    d = LeakyReLU()(d)
    # 輸出層
    output = Dense(n_inputs, activation='linear')(d)
    # 完整的autoencoder模型
    model = Model(inputs=visible, outputs=output)

    # 編譯
    model.compile(optimizer='adam', loss='mse')
    # 回呼函式
    callbacks = EarlyStopping(patience = 20, restore_best_weights = True)
    # 訓練模型
    model.fit(lyric_tr, lyric_tr, epochs=200,
                        batch_size=16, verbose=1, validation_split=0.2,
             callbacks = callbacks)
    
    # 在降維階段,我們只用encoder部分就可以(對資料進行壓縮)
    encoder = Model(inputs=visible, outputs=bottleneck)

    return encoder

# 資料預處理函式,主要是對特征列進行降維,標簽列進行編碼
def pre_process(train = pd.DataFrame,
                test = pd.DataFrame,
                reduction_method = "pca",
                n_components = 400):
    # 切分X和y
    y_train = train.playlist_genre
    y_test = test.playlist_genre
    X_train = train.drop(columns = "playlist_genre")
    X_test = test.drop(columns = "playlist_genre")
    
    # 標簽編碼為數字
    label_encoder = LabelEncoder()
    label_train = label_encoder.fit_transform(y_train)
    label_test = label_encoder.transform(y_test)

    # 對資料進行幅度縮放處理
    scaler = MinMaxScaler()
    X_norm_tr = scaler.fit_transform(X_train)
    X_norm_te = scaler.transform(X_test)

    # 重建資料
    X_norm_tr = pd.DataFrame(X_norm_tr, columns = X_train.columns)
    X_norm_te = pd.DataFrame(X_norm_te, columns = X_test.columns)

    # mode和key都設定為類別型
    X_norm_tr["audio_mode"] = X_train["audio_mode"].astype("category").reset_index(drop = True)
    X_norm_tr["audio_key"] = X_train["audio_key"].astype("category").reset_index(drop = True)
    X_norm_te["audio_mode"] = X_test["audio_mode"].astype("category").reset_index(drop = True)
    X_norm_te["audio_key"] = X_test["audio_key"].astype("category").reset_index(drop = True)
    
    # 歌詞特征
    lyric_tr = X_norm_tr.loc[:, "lyrics_aah":]
    lyric_te = X_norm_te.loc[:, "lyrics_aah":]

    # 如果使用PCA降維
    if reduction_method == "pca":
        pca = PCA(n_components)
        # 擬合訓練集
        reduced_tr = pd.DataFrame(pca.fit_transform(lyric_tr)).add_prefix("lyrics_pca_")
        # 對測驗集變換(降維)
        reduced_te = pd.DataFrame(pca.transform(lyric_te)).add_prefix("lyrics_pca_")
    
    # 如果使用SVD降維
    if reduction_method == "svd":
        svd = TruncatedSVD(n_components)
        # 擬合訓練集
        reduced_tr = pd.DataFrame(svd.fit_transform(lyric_tr)).add_prefix("lyrics_svd_")
        # 對測驗集變換(降維)
        reduced_te = pd.DataFrame(svd.transform(lyric_te)).add_prefix("lyrics_svd_")
    
    # 如果使用自編碼器降維(注意,神經網路的訓練時間會長一點,要耐心等待)
    if reduction_method == "keras":
        # 構建自編碼器
        encoder = autoencode(lyric_tr, n_components)
        
        # 通過編碼器部分進行資料降維
        reduced_tr = pd.DataFrame(encoder.predict(lyric_tr)).add_prefix("lyrics_keras_")
        reduced_te = pd.DataFrame(encoder.predict(lyric_te)).add_prefix("lyrics_keras_")

        
        
    # 合并降維后的歌詞特征與音頻特征
    X_norm_tr = pd.concat([X_norm_tr.loc[:, :"audio_duration_ms"],
                          reduced_tr
                          ], axis = 1)

    X_norm_te = pd.concat([X_norm_te.loc[:, :"audio_duration_ms"],
                           reduced_te
                           ], axis = 1)


    return X_norm_tr, label_train, X_norm_te, label_test, label_encoder


# 分層切分資料
train_raw, test_raw = train_test_split(data, test_size = 0.2,
                                       shuffle = True, random_state = 42, # random, reproducible split
                                       stratify = data.playlist_genre)
# 設定降維最終維度
n_components = 500
# 選擇降維方法,候選: "pca", "svd", "keras"
reduction_method = "pca"

# 完整的資料預處理
X_train, y_train, X_test, y_test, label_encoder = pre_process(train_raw, test_raw,
                                                      reduction_method = reduction_method,
                                                     n_components = n_components)

上述程序之后我們已經完成對資料的標準化、編碼轉換和降維,接下來我們使用它進行建模,

?? 建模和超引數優化

?? 構建模型

在實際建模之前,我們要先選定一個評估指標來評估我們模型的性能,也方便指導進一步的優化,由于我們資料最終的標簽『流派/類別』略有不平衡,宏觀 F1 分數(macro f1-score) 可能是一個不錯的選擇,因為它平等地評估了類別的貢獻,我們在下面對這個評估準則進行定義,也敲定 LightGBM 模型的部分超引數,

from sklearn.metrics import f1_score

# 定義評估準則(Macro F1)
def lgb_f1_score(preds, data):
    labels = data.get_label()
    preds = preds.reshape(5, -1).T
    preds = preds.argmax(axis = 1)
    f_score = f1_score(labels , preds,  average = 'macro')
    return 'f1_score', f_score, True

# 用于編譯的引數
fixed_params = {
        'objective': 'multiclass',
        'metric': "None",   # 我們自定義的f1-score可以應用
        'num_class': 5,
        'verbosity': -1,
}

LightGBM 帶有大量可調超引數,這些超引數對于最終效果影響很大,

關于 LightGBM 的超引數細節詳細講解,歡迎大家查閱 ShowMeAI 的文章:

??機器學習實戰(5) | LightGBM建模應用詳解

下面我們會基于Optuna這個工具庫對 LightGBM 的超引數進行調優,我們需要在 param 定義超引數的搜索空間,在此基礎上 Optuna 會進行優化和超引數的選擇,


# 建模
from sklearn.model_selection import StratifiedKFold
import lightgbm as lgb
from optuna.integration import LightGBMPruningCallback

# 定義目標函式
def objective(trial, X, y):    
    # 候選超引數
    param = {**fixed_params,
        'boosting_type': 'gbdt',
        'num_leaves': trial.suggest_int('num_leaves', 2, 3000, step = 20),
        'feature_fraction': trial.suggest_float('feature_fraction', 0.2, 0.99, step = 0.05),
        'bagging_fraction': trial.suggest_float('bagging_fraction', 0.2, 0.99, step = 0.05),
        'bagging_freq': trial.suggest_int('bagging_freq', 1, 7),
        'min_child_samples': trial.suggest_int('min_child_samples', 5, 100),
        "n_estimators": trial.suggest_int("n_estimators", 200, 5000),
        "learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3),
        "max_depth": trial.suggest_int("max_depth", 3, 12),
        "min_data_in_leaf": trial.suggest_int("min_data_in_leaf", 5, 2000, step=5),
        "lambda_l1": trial.suggest_float("lambda_l1", 1e-8, 10.0, log=True),
        "lambda_l2": trial.suggest_float("lambda_l2", 1e-8, 10.0, log=True),
        "min_gain_to_split": trial.suggest_float("min_gain_to_split", 0, 10),
        "max_bin": trial.suggest_int("max_bin", 200, 300),
    }
    
    # 構建分層交叉驗證
    cv = StratifiedKFold(n_splits = 5, shuffle = True)
    # 5組得分
    cv_scores = np.empty(5)
    
    # 切分為K個資料組,輪番作為訓練集和驗證集進行實驗
    for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
        # 資料切分
        X_train_cv, X_test_cv = X.iloc[train_idx], X.iloc[test_idx]
        y_train_cv, y_test_cv = y[train_idx], y[test_idx]

        # 轉為lightgbm的Dataset格式
        train_data = https://www.cnblogs.com/showmeai/p/lgb.Dataset(X_train_cv, label = y_train_cv, categorical_feature="auto")
        val_data = https://www.cnblogs.com/showmeai/p/lgb.Dataset(X_test_cv, label = y_test_cv,  categorical_feature="auto",
                              reference = train_data)
        
        # 回呼函式
        callbacks = [
            LightGBMPruningCallback(trial, metric = "f1_score"),
                     # 間歇輸出資訊
                    lgb.log_evaluation(period = 100),
                     # 早停止,防止過擬合
                    lgb.early_stopping(50)]

        # 訓練模型
        model = lgb.train(params = param,  train_set = train_data,
                          valid_sets = val_data,   
                          callbacks = callbacks,
                          feval = lgb_f1_score # 自定義評估準則
                         )
        
        # 預估
        preds = np.argmax(model.predict(X_test_cv), axis = 1)
        # 計算f1-score
        cv_scores[idx] = f1_score(y_test_cv, preds, average = "macro")

    return np.mean(cv_scores)

?? 超引數優化

我們在上面定義完了目標函式,現在可以使用 Optuna 來調優模型的超引數了,

# 超引數優化
import optuna

# 定義Optuna的實驗次數
n_trials = 200
# 構建Optuna study去進行超引數檢索與調優
study = optuna.create_study(direction = "maximize", # 最大化交叉驗證的F1得分
                            study_name = "LGBM Classifier",
                           pruner=optuna.pruners.HyperbandPruner())
func = lambda trial: objective(trial, X_train, y_train)
study.optimize(func, n_trials = n_trials)

然后,我們可以使用 ??Optuna 的可視化模塊不同超引陣列合的性能進行可視化查看,例如,我們可以使用 plot_param_importances(study) 查看哪些超引數對模型性能/影響優化最重要,

plot_param_importances(study)

我們也可以使用 plot_parallel_coordinate(study)查看嘗試了哪些超引陣列合/范圍可以帶來高評估結果值(好的效果性能),

plot_parallel_coordinate(study)

然后我們可以使用 plot_optimization_history 查看歷史情況,

plot_optimization_history(study)

在Optuna完成調優之后:

  • 最好的超引數存盤在 study.best_params 屬性中,我們把模型的最終引數 params 定義為 params = {**fixed_params, **study.best_params} 即可,如后續的代碼所示,
  • 當然,你也可以縮小搜索空間/超引數范圍,進一步做精確的超引數優化,
# 最佳模型實驗
cv = StratifiedKFold(n_splits = 5, shuffle = True)
# 5組得分
cv_scores = np.empty(5)

# 切分為K個資料組,輪番作為訓練集和驗證集進行實驗
for idx, (train_idx, test_idx) in enumerate(cv.split(X, y)):
    # 資料切分
    X_train_cv, X_test_cv = X.iloc[train_idx], X.iloc[test_idx]
    y_train_cv, y_test_cv = y[train_idx], y[test_idx]

    # 轉為lightgbm的Dataset格式
    train_data = https://www.cnblogs.com/showmeai/p/lgb.Dataset(X_train_cv, label = y_train_cv, categorical_feature="auto")
    val_data = https://www.cnblogs.com/showmeai/p/lgb.Dataset(X_test_cv, label = y_test_cv,  categorical_feature="auto",
                          reference = train_data)
    
    # 回呼函式
    callbacks = [
        LightGBMPruningCallback(trial, metric = "f1_score"),
                 # 間歇輸出資訊
                lgb.log_evaluation(period = 100),
                 # 早停止,防止過擬合
                lgb.early_stopping(50)]

    # 訓練模型
    model = lgb.train(params = {**fixed_params, **study.best_params},  train_set = train_data,
                      valid_sets = val_data,   
                      callbacks = callbacks,
                      feval = lgb_f1_score # 自定義評估準則
                     )
    
    # 預估
    preds = np.argmax(model.predict(X_test_cv), axis = 1)
    # 計算f1-score
    cv_scores[idx] = f1_score(y_test_cv, preds, average = "macro")

?? 最終評估

通過上述程序我們就獲得了最終模型,讓我們來評估一下吧!


# 預估與評估訓練集
train_preds = model.predict(X_train)
train_predictions = np.argmax(train_preds, axis = 1)
train_error = f1_score(y_train, train_predictions, average = "macro")

# 交叉驗證結果
cv_error = np.mean(cv_scores)

# 評估測驗集
test_preds = model.predict(X_test)
test_predictions = np.argmax(test_preds, axis = 1)
test_error = f1_score(y_test, test_predictions, average = "macro")

# 存盤評估結果
results = pd.DataFrame({"n_components": n_components,
                        "reduction_method": reduction_method,
                        "train_error": train_error,
                        "cv_error": cv_error,
                        "test_error": test_error,
                        "n_trials": n_trials
                       }, index = [0])

我們可以實驗和比較不同的降維方法、降維維度,再調參查看模型效果,如下圖所示,在我們當前的嘗試中,PCA降維到 400 維產出最好的模型 ——macro f1-score 為66.48%,

?? 總結

在本篇內容中, ShowMeAI 展示了基于歌曲資訊與文本對其進行『流派』分類的程序,包含對文本資料的處理、特征工程、模型建模和超引數優化等,大家可以把整個pipeline作為一個模板來應用在其他任務當中,

參考資料

  • ?? 圖解資料分析:從入門到精通系列教程:https://www.showmeai.tech/tutorials/3
  • ?? 資料科學工具庫速查表 | Pandas 速查表:https://www.showmeai.tech/article-detail/101
  • ?? 圖解機器學習演算法 | 降維演算法詳解:https://www.showmeai.tech/article-detail/198
  • ?? 圖解機器學習演算法 | LightGBM模型詳解:https://www.showmeai.tech/article-detail/195
  • ?? 機器學習實戰 | LightGBM建模應用詳解:https://www.showmeai.tech/article-detail/205
  • ?? Optuna 的可視化模塊
  • ?? Akiba,T., Sano, S., Yanase, T., Ohta, T., & Koyama, M. (2019, July). Optuna: A next-generation hyperparameter optimization framework. In Proceedings of the 25th ACM SIGKDD international conference on knowledge discovery & data mining (pp. 2623–2631).
  • ?? Autoencoder Feature Extractions
  • ?? Kaggler’s Guide to LightGBM Hyperparameter Tuning with Optuna in 2021
  • ?? You Are Missing Out on LightGBM. It Crushes XGBoost in Every Aspect

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/520638.html

標籤:其他

上一篇:Shell腳本1

下一篇:用昇騰AI護航“井下安全”

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more