模型學(xué)習(xí)曲線

一般來(lái)說(shuō)學(xué)習(xí)曲線:一種用來(lái)判斷訓(xùn)練模型的一種方法通過(guò)查看學(xué)習(xí)曲線,可以對(duì)模型的狀態(tài)進(jìn)行判斷慕的。

1.偏差的方差:

\color{red}{1.偏差(bias)}: 偏差度量了學(xué)習(xí)算法的期望預(yù)測(cè)與真實(shí)結(jié)果的偏離程序, 即\color{#8FBC8F}{刻畫了學(xué)習(xí)算法本身的擬合能力}
\color{red}{2.方差(variance)}: 方差度量了同樣大小的訓(xùn)練集的變動(dòng)所導(dǎo)致的學(xué)習(xí)性能的變化, 即 \color{#8FBC8F}{模型的泛化能力}

請(qǐng)看下圖:

分類器偏差方差關(guān)系

,一般稱為欠擬合(underfitting),即我們的模型并沒(méi)有很好的去適配現(xiàn)有的數(shù)據(jù)筷频,擬合度不夠。
)的情況一般稱作過(guò)擬合(overfitting)前痘,即模型對(duì)于訓(xùn)練數(shù)據(jù)擬合度太高了凛捏,失去了泛化的能力。

2.learning curve

如何判斷模型是否合理?
判斷模型擬合程度
這里可以使用learning curve幫我們判斷

上圖分別對(duì)應(yīng)了欠擬合芹缔,過(guò)擬合和合理學(xué)習(xí)曲線坯癣,這里需要解釋一下,橫坐標(biāo)表示訓(xùn)練集大小最欠,縱坐標(biāo)表示metric score示罗。\color{#8FBC8F}{分別選取訓(xùn)練集的部分樣本訓(xùn)練模型,然后驗(yàn)證得分}芝硬。如下圖所示:

學(xué)習(xí)曲線訓(xùn)練方法

\color{red}{高偏差(high bias)}時(shí)蚜点,訓(xùn)練集和驗(yàn)證集的cv結(jié)果隨著訓(xùn)練集數(shù)據(jù)增大逐漸收斂,但是正確率遠(yuǎn)遠(yuǎn)于我們期望的情況拌阴,這時(shí)屬于\color{#1E90FF}{欠擬合}绍绘,可以通過(guò): 訓(xùn)練更長(zhǎng)時(shí)間, 增加樣本, 增加特征、增加樹(shù)的深度皮官、減小正則項(xiàng)脯倒,增加網(wǎng)絡(luò)結(jié)構(gòu),如增加隱藏層數(shù)目捺氢, 尋找合適的網(wǎng)絡(luò)架構(gòu)藻丢,使用更大的NN結(jié)構(gòu) 來(lái)解決

\color{red}{高方差(high variance)}時(shí),訓(xùn)練集上面的cv結(jié)果非常高摄乒,但是驗(yàn)證集的結(jié)果就不是那么理想悠反,這時(shí)屬于\color{#CD5C5C}{過(guò)擬合},可以使用更多的數(shù)據(jù)馍佑,正則化(regularization)斋否, 尋找合適的網(wǎng)絡(luò)結(jié)構(gòu),樹(shù)的剪枝來(lái)解決拭荤。

一般來(lái)說(shuō)茵臭,過(guò)擬合的情況出現(xiàn)的會(huì)比較多一些,除非在計(jì)算資源很缺乏舅世,一般不會(huì)出現(xiàn)欠擬合的情況

這里可以使用sklearn learning curve這個(gè)函數(shù), 學(xué)習(xí)曲線請(qǐng)看下圖:

學(xué)習(xí)曲線

上圖中旦委,深色的線表示cv后的metric的均值奇徒,前色線表示浮動(dòng)的方差,這里時(shí)使用了matplotlib畫的靜態(tài)圖缨硝,我使用plotly改寫的一下變成摩钙,更方面觀看:

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     aaa
   Description :
   Author :        Asdil
   date:          2020/3/26
-------------------------------------------------
   Change Activity:
                   2020/3/26:
-------------------------------------------------
"""
__author__ = 'Asdil'
import plotly
import plotly.io as pio
import plotly.offline as py  # 離線模式
from plotly.graph_objs import Data, Figure
plotly.offline.init_notebook_mode(connected=True)
import warnings
warnings.filterwarnings('ignore')

def add_trace(percent, score_means, score_std, is_train=True):
    """add_trace方法用于添加每輪驗(yàn)證結(jié)果

    Parameters
    ----------
    percent : list
        訓(xùn)練數(shù)據(jù)集百分比 eg: [0, 0.25, 0.5, 0.75, 1]
    score_means : list
        訓(xùn)練百分n的數(shù)據(jù)后, cv得分均值
    score_std : list
        訓(xùn)練百分n的數(shù)據(jù)后, cv得分均方差
    is_train : bool
        True 或者 False
    Returns
    ----------
    """
    score_means = np.array(score_means)
    score_std = np.array(score_std)

    name = 'Train score' if is_train else 'Test score'
    color_std = "rgba(211, 47, 47, 0.4)" if is_train else "rgba(56, 142, 60, 0.4)"
    color_mean = "#d32f2f" if is_train else "#388E3C"
    trace_mean = {
        "line": {"color": color_mean},
        "name": name,
        "type": "scatter",
        "x": percent,
        "y": score_means,
        "showlegend": True
    }
    trace_mean_add_std = {
        "fill": "tonexty",
        "line": {
            "color": color_std,
            "width": 0.1
        },
        "mode": "lines",
        "name": "",
        "type": "scatter",
        "x": percent,
        "y": score_means+score_std,
        "hoverlabel": {"namelength": 20},
        "showlegend": False
    }
    trace_mean_sub_std = {
        "line": {
            "color": color_std,
            "width": 0.1
        },
        "mode": "lines",
        "name": "",
        "type": "scatter",
        "x": percent,
        "y": score_means-score_std,
        "hoverlabel": {"namelength": 20},
        "showlegend": False
    }

    return [trace_mean_sub_std, trace_mean_add_std, trace_mean]


def create_lay_out(score_name, title='Learning curve'):
    """create_lay_out方法用于添加layout注釋

    Parameters
    ----------
    title : str
        標(biāo)題
    score_name : str
        metric 函數(shù)名稱 F1, ACC ect...
    Returns
    ----------
    """
    layout = {
        "title": {"text": f"{title}"},
        "xaxis": {"title": {"text": "訓(xùn)練集樣本百分比"}},
        "yaxis": {"title": {"text": f"{score_name}"}},
        "legend": {
            "x": 0.8,
            "y": 0
        },
        "autosize": True
    }
    return layout
from sklearn.naive_bayes import GaussianNB
import numpy as np
from sklearn.svm import SVC
from sklearn.datasets import load_digits
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit

# 這里使用ShuffleSplit 對(duì)訓(xùn)練集合劃分
cv = ShuffleSplit(n_splits=100, test_size=0.2, random_state=0)
X, y = load_digits(return_X_y=True) # 使用手寫體數(shù)據(jù)集minst
estimator = SVC(gamma=0.001) # smv模型

percent = np.linspace(.1, 1.0, 5) # 注意這里將訓(xùn)練集按照10%-100%劃分成5份,對(duì)應(yīng)結(jié)果圖片x坐標(biāo)有5個(gè)
scoring = 'f1_macro' # f1_macro accuracy 
train_sizes, train_scores, test_scores, fit_times, _ = learning_curve(estimator, 
                                                                      X, 
                                                                      y, 
                                                                      cv=cv, 
                                                                      n_jobs=-1,
                                                                      train_sizes=percent,
                                                                      scoring = scoring,
                                                                      return_times=True)

# 獲取5次訓(xùn)練集f1得分均值和方差查辩,驗(yàn)證集5次f1均值和方差
train_scores_mean = np.mean(train_scores, axis=1)
train_scores_std = np.std(train_scores, axis=1)
test_scores_mean = np.mean(test_scores, axis=1)
test_scores_std = np.std(test_scores, axis=1)
# 這是訓(xùn)練集分批cv結(jié)果的那條線
train_trace = add_trace(percent=percent, 
                        score_means=train_scores_mean, 
                        score_std=train_scores_std, is_train=True)
# 這是測(cè)試集集cv結(jié)果的那條線
test_trace = add_trace(percent=percent, 
                        score_means=test_scores_mean, 
                        score_std=test_scores_std, is_train=False)

layout = create_lay_out(score_name=scoring, title='Learning curve of SVC')
data = Data([*train_trace, *test_trace])
fig = Figure(data=data, layout=layout)
py.iplot(fig)
# pio.write_html(fig, file='iris.html')
學(xué)習(xí)曲線動(dòng)態(tài)圖

3.損失函數(shù)值可視化:

上面的例子小樣本集中是可以嘗試的胖笛,但是通常情況下,不會(huì)有那么多的自己算資源宜岛,假設(shè)一個(gè)模型需要運(yùn)行2天长踊,我們將訓(xùn)練集拆分成不同百分比驗(yàn)證模型顯然是不可取的,因?yàn)闀r(shí)間消耗太大谬返。更多的時(shí)候我們需要看訓(xùn)練集cv和驗(yàn)證集cv的曲線變化之斯,這里介紹另一個(gè)可以實(shí)時(shí)查看損失和metrc的包,名叫lrcurve遣铝,需要注意的是這個(gè)包需要安裝tensorlow
這里使用一個(gè)pytorch的例子佑刷,這里使用一個(gè)手寫體識(shí)別的例子,在例子中我們會(huì)\color{#CD5C5C}{實(shí)時(shí)}顯示出訓(xùn)練集和測(cè)試集的損失函數(shù)和metric

from lrcurve import PlotLearningCurve
import torch
from torchvision import datasets, transforms
import torch.nn as nn
from torch import optim
import torch.nn.functional as F
import torchvision.transforms as transforms
from sklearn.metrics import accuracy_score, f1_score
import numpy as np
transform = transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.5,), (0.5,)),
                              ])
# 下載數(shù)據(jù)集可能需要花一點(diǎn)時(shí)間
trainset = datasets.MNIST('./mnist', download=True, train=True, transform=transform)
valset = datasets.MNIST('./mnist', download=True, train=False, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
valloader = torch.utils.data.DataLoader(valset, batch_size=64, shuffle=True)
dataiter = iter(trainloader)
images, labels = dataiter.next()
input_size = 784
hidden_sizes = [64, 32]
output_size = 10
# 定義一個(gè)簡(jiǎn)單的全連接模型
model = nn.Sequential(nn.Linear(input_size, hidden_sizes[0]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[0], hidden_sizes[1]),
                      nn.ReLU(),
                      nn.Linear(hidden_sizes[1], output_size),
                      nn.LogSoftmax(dim=1))
criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.003, momentum=0.9)
epochs = 15 # 進(jìn)行15輪
plot = PlotLearningCurve(
   # 這里定義三幅圖酿炸,分別是損失函數(shù)圖, accuracy曲線和f1 socre曲線
    facet_config = {
        'loss': { 'name': 'NLLLoss', 'limit': [0, None] },
        'accuracy': { 'name': 'Accuracy', 'limit': [0, 1] }, # acc 和 f1 取值都是0-1
        'f1_score': { 'name': 'F1_score', 'limit': [0, 1] }

    },
    xaxis_config = { 'name': 'Epoch', 'limit': [0, 15] } # 這里定義x軸瘫絮,我們迭代epochs = 15輪,因此這里是0-15
)
# 將整個(gè)pytorch訓(xùn)練過(guò)程包在plot中
with plot:
    for epoch in range(epochs):
        train_loss = 0
        train_metric_1 = 0
        train_metric_2 = 0
        for images, labels in trainloader:
            images = images.view(images.shape[0], -1)
            optimizer.zero_grad()
            output = model(images)
            loss = criterion(output, labels)
            loss.backward()
            optimizer.step()
            train_loss += loss.item()
           #記錄每個(gè)epoch中每個(gè)batch的acc和f1
            train_metric_1 += f1_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy(), 
                                       average='macro')
            train_metric_2 += accuracy_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy())
        model.eval() # eval mode
        val_loss = 0
        val_metric_1 = 0
        val_metric_2 = 0
        with torch.no_grad():
            for images, labels in valloader:
                images = images.view(images.shape[0], -1)
                output = model(images)
                loss = criterion(output, labels)
                val_loss += loss.item()
                 #記錄每個(gè)epoch中每個(gè)batch的acc和f1
                val_metric_1 += f1_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy(), 
                                         average='macro')
                val_metric_2 += accuracy_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy())
        # 求出所有batch平均值
        train_metric_1 = train_metric_1/len(trainloader)  
        train_metric_2 = train_metric_2/len(trainloader)   
        val_metric_1 = val_metric_1/len(valloader)
        val_metric_2 = val_metric_2/len(valloader)
        # 將結(jié)果添加到學(xué)習(xí)曲線中以顯示
        plot.append(epoch, {
            'loss': {
                'train': train_loss/len(trainloader), # 每個(gè)epoch所有batch損失函數(shù)均值
                'validation': val_loss/len(valloader),
            },
            'accuracy': {
                'train': train_metric_2, # 訓(xùn)練集acc均值
                'validation': val_metric_2
            },
            'f1_score': {
                'train': train_metric_1,  # 訓(xùn)練集f1數(shù)均值
                'validation': val_metric_1
            },
        })
        plot.draw() # 每一個(gè)epoch將數(shù)據(jù)顯示出來(lái)
學(xué)習(xí)曲線

這實(shí)際上是一個(gè)動(dòng)圖填硕,可以參考下面這張圖


學(xué)習(xí)曲線動(dòng)圖演示

4.使用tensorboard

還有一種方法可視化損失函數(shù)和metric那就是使用tensorboard麦萤,方法也很簡(jiǎn)單,還是上面那個(gè)例子,前面的定義都一樣:

# 應(yīng)用tensorboard
from torch.utils.tensorboard import SummaryWriter
# default `log_dir` is "runs" - we'll be more specific here
writer = SummaryWriter('runs/fashion_mnist_experiment_1') # 日志路徑

for epoch in range(epochs):
    train_loss = 0
    train_metric_1 = 0
    train_metric_2 = 0
    for images, labels in trainloader:
        images = images.view(images.shape[0], -1)
        optimizer.zero_grad()
        output = model(images)
        loss = criterion(output, labels)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()
        train_metric_1 += f1_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy(), 
                                       average='macro')
        train_metric_2 += accuracy_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy())
    model.eval() # eval mode
    val_loss = 0
    val_metric_1 = 0
    val_metric_2 = 0
    with torch.no_grad():
        for images, labels in valloader:
            images = images.view(images.shape[0], -1)
            output = model(images)
            loss = criterion(output, labels)
            val_loss += loss.item()
            val_metric_1 += f1_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy(), 
                                         average='macro')
            val_metric_2 += accuracy_score(np.argmax(output.detach().numpy(), axis=1), labels.detach().numpy())

    train_metric_1 = train_metric_1/len(trainloader)  
    train_metric_2 = train_metric_2/len(trainloader)   
    val_metric_1 = val_metric_1/len(valloader)
    val_metric_2 = val_metric_2/len(valloader)
    # 這里把信息寫入tensorboard
    writer.add_scalars('loss',
                       {'train loss':train_loss/len(trainloader), 
                        'val_loss:':val_loss/len(valloader)},epoch)
        
writer.close()
# 這里直接在jupyter中顯示
%load_ext tensorboard
%tensorboard --logdir='runs/fashion_mnist_experiment_1'
tensorboard顯示學(xué)習(xí)曲線
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扁眯,隨后出現(xiàn)的幾起案子壮莹,更是在濱河造成了極大的恐慌,老刑警劉巖姻檀,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件命满,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绣版,警方通過(guò)查閱死者的電腦和手機(jī)胶台,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杂抽,“玉大人诈唬,你說(shuō)我怎么就攤上這事∷豸铮” “怎么了铸磅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我愚屁,道長(zhǎng)济竹,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任霎槐,我火速辦了婚禮,結(jié)果婚禮上梦谜,老公的妹妹穿的比我還像新娘丘跌。我一直安慰自己,他們只是感情好唁桩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布闭树。 她就那樣靜靜地躺著,像睡著了一般荒澡。 火紅的嫁衣襯著肌膚如雪报辱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天单山,我揣著相機(jī)與錄音碍现,去河邊找鬼。 笑死米奸,一個(gè)胖子當(dāng)著我的面吹牛昼接,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悴晰,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼慢睡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了铡溪?” 一聲冷哼從身側(cè)響起漂辐,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棕硫,沒(méi)想到半個(gè)月后髓涯,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饲帅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年复凳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灶泵。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡育八,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赦邻,到底是詐尸還是另有隱情髓棋,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站按声,受9級(jí)特大地震影響膳犹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜签则,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一须床、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渐裂,春花似錦豺旬、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至膝捞,卻和暖如春坦刀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔬咬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工鲤遥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人计盒。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓渴频,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親北启。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卜朗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容