Այս հոդվածում մենք կփորձենք հասկանալ մեքենայական ուսուցման հիմքում ընկած որոշ հիմնական հասկացություններ՝ ձեռքով իրականացնելով գծային ռեգրեսիայի ալգորիթմ:
Գծային ռեգրեսիան ներառում է մի շարք կետերի միջոցով լավագույն համապատասխանության գիծը գտնելը: Այն ի վիճակի է սովորել կապը հատկանիշի կամ մի շարք հատկանիշների և թիրախային փոփոխականի միջև: Օրինակ, այն կարող է օգտագործվել՝ կանխատեսելու այնպիսի հատկանիշների ազդեցությունը, ինչպիսիք են խոնավությունը և ջերմաստիճանը թիրախային փոփոխականի վրա, ինչպիսին տեղումներն են:
Տվյալների հավաքածուի ստեղծում
Գծային ռեգրեսիան պահանջում է պիտակավորված տվյալների բազա, որը պարունակում է x արժեքների շարք և համապատասխան y արժեքներ: Այս օրինակի համար մենք կօգտագործենք տվյալների բազա, որը ստեղծվել է scikit-learn գրադարանի միջոցով:
from sklearn.datasets import make_regression # to generate toy dataset import matplotlib.pyplot as plt # to visualise data import numpy as np # for arrays xvals, yvals = make_regression(n_samples=200, n_features=1, noise=40, random_state=0) xvals, yvals = xvals.reshape(xvals.shape[0]), yvals.reshape(yvals.shape[0]) # reshaping to correct shape # n_samples = number of data points in dataset (x-y paired values) # n_features = number of features (x values) affecting each y value # noise = strength of correlation between x and y. Higher value indicates lower strength.
Այս պահին xvals-ը և yval-ը լողացող արժեքներ պարունակող երկու զանգված են: Երբ գծագրվում են, նրանք արտադրում են հետևյալ գրաֆիկը.
plt.scatter(xvals,yvals) # plot x and y values plt.xlabel("x values") # label x-axis plt.ylabel("y values") # label y-axis plt.show() # show graph
Ինչպես տեսնում ենք, x և y արժեքների միջև կա դրական հարաբերակցություն: Մեր նպատակն է գտնել այս տվյալների հավաքածուի համար լավագույն պիտանի գիծը, որպեսզի մենք ի վերջո կարողանանք կանխատեսել y արժեքները՝ հաշվի առնելով x արժեքները:
Գտնելով լավագույն պիտանի գիծը
Ցանկացած երկչափ գիծ կարելի է սահմանել հետևյալ հավասարմամբ.
Այս հավասարման մեջ wուղիների գրադիենտն է, իսկ bը` y-հատվածը: Այս դեպքում մենք նկատի կունենանք w որպես **քաշ** և b որպես **կողմնակալություն**: Հետևաբար, լավագույն համապատասխանության գիծը գտնելը պարզապես նշանակում է գտնել w և b իդեալական արժեքները: Մենք կսկսենք օգտագործելով պատահական արժեքներ wև b-ի համար՝ կրկնելով դրանք, մինչև հասնենք օպտիմալ արժեքներին:
np.random.seed(seed=0) w = np.random.rand(1)[0] b = np.random.rand(1)[0]
Կանխատեսված y արժեքը (կոչվում է y-hat) այս արժեքներն օգտագործելիս wև b-ի համար տրվում է. ;
Ահա մինչ այժմ փոփոխականների ամփոփագիրը.
Եկեք հիմա սահմանենք մի քանի գործառույթ՝ այս գիծը մեզ համար լավագույնս հարմարեցնելու համար:
def forward(w,b,x): return (w*x) + b def plot_data(xvals, yvals, w, b): plt.scatter(xvals, yvals) # plotting dataset plt.xlabel("x values") plt.ylabel("y values") y_start = forward(w, b, xvals.min()) y_end = forward(w, b, xvals.max()) plt.plot([xvals.min(), xvals.max()], [y_start, y_end], c="red") # plotting line from start to end coordinates plt.show() plot_data(xvals, yvals, w, b)
Սա առաջացնում է հետևյալ գրաֆիկը.
Ավելորդ է ասել, որ սա լավագույն պիտանի գիծ չէ: Այնուամենայնիվ, քանի որ մենք գիտենք y-ի իդեալական արժեքները, անշուշտ, մենք կարող ենք դրանք ինչ-որ կերպ օգտագործել մոդելին «հետադարձ կապ» ապահովելու համար, որպեսզի այն կարողանա հարմարեցնել դրա քաշը և կողմնակալությունը: Այս «հետադարձ կապի» ֆունկցիան այն է, ինչ մենք անվանում ենք կորստի ֆունկցիա:
Կորստի գործառույթ
Կորստի ֆունկցիան քանակականացնում է, թե որքանով են մեր կանխատեսումները շեղվում իրական տվյալներից: Կան բազմաթիվ կորստի գործառույթներ, որոնք կարող են օգտագործվել ռեգրեսիայի համար, բայց դրանց ետևում գտնվող ինտուիցիան մնում է նույնը: Մենք սկսում ենք մնացորդը հաշվարկելով
Մնացորդը պարզապես վերցնում է ուղղահայաց հեռավորությունը մեկ կանխատեսված y-hat արժեքի և իրական y արժեքի միջև: Այնուհետև սա քառակուսի է դառնում, որպեսզի բոլոր մնացորդները դառնան դրական՝ անկախ նրանից, թե y-ը մեծ է, թե փոքր, քան y-ը:
Կոնկրետ կորստի ֆունկցիան, որը մենք կօգտագործենք, միջին քառակուսի սխալն է (MSE), որը ներառում է այս մնացորդների միջին արժեքի հաշվարկը ամբողջ տվյալների վրա:
def mse_loss(yhat, y): return ((yhat-y)**2).mean()
Այստեղից մենք կարող ենք ինտուիտիվ կերպով ասել, որ որքան ցածր է կորստի արժեքը, այնքան ավելի լավ են մեր կանխատեսումները: Այժմ մենք ավելի մաթեմատիկական նպատակ ունենք. նվազագույնի հասցնելով կորստի ֆունկցիայի արժեքը՝ գտնելով քաշի և կողմնակալության օպտիմալ արժեքները:
Քաշի և կողմնակալության արժեքների կարգավորում
Քաշի և կողմնակալության արժեքների օպտիմալացման գործընթացի երեք հիմնական քայլ կա:
- Առաջ տարածում
- Հետ տարածում
- Պարամետրերի արժեքների թարմացում
Առաջ տարածում
Այս քայլում մենք մոդելի միջով անցնում ենք x արժեքների ամբողջ հավաքածուն և ստանում կանխատեսված y-hat արժեքները: Այնուհետև մենք հաշվարկում ենք MSE կորուստը:
Հետ տարածում
Սա, թերեւս, գծային ռեգրեսիայի ամենակարեւոր քայլն է: Այս քայլում մենք հաշվարկում ենք երկու բան.
- Կորուստի գրադիենտը wի նկատմամբ
- Կորստի գրադիենտը բի նկատմամբ
Տարբերակելով Lը y-hat-ի նկատմամբ՝ ստանում ենք;
Տարբերակելով y-hat w-ի և bի նկատմամբ՝ մենք ստանում ենք.
Հետևաբար, կիրառելով շղթայի կանոնը, ունենք.
Սա կարող է թարգմանվել python-ի հետևյալ կոդով.
def w_grad(xvals, yhat_vals, yvals): # Gradient of L wrt w sum_ = np.sum((yhat_vals - yvals)* xvals) return sum_ * (2/len(xvals)) def b_grad(yhat_vals, yvals): # Gradient of L wrt b sum_ = np.sum(yhat_vals - yvals) return sum_ * (2/len(yvals))
Պարամետրերի թարմացում
Այժմ, երբ մենք հաշվարկել ենք գրադիենտները, մենք պետք է օգտագործենք դրանք՝ թարմացնելու համար w և b պարամետրերը:
Մենք գիտենք, որ բացասական գրադիենտը ենթադրում է, որ ֆունկցիայի y արժեքը նվազում է, քանի որ x արժեքը մեծանում է և հակառակը: Օգտագործելով սա՝ մենք գիտենք, որ կորուստը նվազագույնի հասցնելու համար մենք պետք է.
1. Բարձրացրեք պարամետրի արժեքը, երբ մեր գրադիենտը բացասական է
2. Կրճատել պարամետրի արժեքը, երբ մեր գրադիենտը դրական է
Հետևաբար, պարամետրերի թարմացման հավասարումները, որոնք հասնում են վերը նշվածին, կարող են գրվել հետևյալ կերպ.
Այստեղ հունական mu տառը ներկայացնում է ուսուցման արագությունը, որը որոշում է պարամետրի արժեքի փոփոխության մեծությունը: Ուսուցման արագությունը չի կարող չափազանց մեծ կամ շատ փոքր լինել, քանի որ դա կարող է առաջացնել պարամետրերի արժեքների անհամապատասխան թարմացումներ, որոնք կարող են դանդաղեցնել կամ ամբողջությամբ կանխել կոնվերգենցիան:
Այս քայլերը կրկնվում են բազմիցս՝ դանդաղորեն կարգավորելու պարամետրերը, մինչև գիծը լավ համապատասխանի տվյալներին: Վերապատրաստման հանգույցը կարելի է տեսնել ստորև:
lr = 0.0007 # learning rate epochs = 2000 # number of iterations fig, ax = plt.subplots(nrows=4, ncols=3, figsize=(22,18)) ax = ax.flatten() ax_counter = 0 for i in range(epochs): yhat_vals = forward(w,b,xvals) # forward pass through model wgrad = w_grad(xvals, yhat_vals, yvals) # calculating gradient L wrt w bgrad = b_grad(yhat_vals, yvals) # calculating gradient L wrt b w = w - (wgrad*lr) # updating w b = b - (bgrad*lr) # updating b if i % ((epochs // 12)+1) == 0 : # plots every 12 iterations ax[ax_counter].scatter(xvals,yvals) y_start = forward(w, b, xvals.min()) y_end = forward(w, b, xvals.max()) ax[ax_counter].plot([xvals.min(), xvals.max()], [y_start, y_end], c='r', linewidth=3) ax[ax_counter].set_title(f'Iteration : {i}') ax_counter += 1
Ինչպես տեսնում ենք վերևի գծապատկերներից, կրկնությունների ընթացքում պարամետրերի արժեքները փոքր-ինչ տեղաշարժվում են ճիշտ ուղղությամբ՝ ի վերջո ստեղծելով գիծ, որը լավ է համապատասխանում տվյալների հավաքածուին:
Այս հոդվածում կատարված ամեն ինչի ամբողջական ծածկագրի նմուշը հետևյալն է.
from sklearn.datasets import make_regression # to generate toy dataset import matplotlib.pyplot as plt # to visualise data import numpy as np # for arrays xvals, yvals = make_regression(n_samples=200, n_features=1, noise=40, random_state=0) xvals, yvals = xvals.reshape(xvals.shape[0]), yvals.reshape(yvals.shape[0]) # reshaping to correct shape # n_samples = number of data points in dataset (x-y paired values) # n_features = number of features (x values) affecting each y value # noise = strength of correlation between x and y. Higher value indicates lower strength. plt.scatter(xvals,yvals) # plot x and y values plt.xlabel("x values") # label x-axis plt.ylabel("y values") # label y-axis plt.show() # show graph np.random.seed(seed=0) w = np.random.rand(1)[0] b = np.random.rand(1)[0] def forward(w,b,x): return (w*x) + b def plot_data(xvals, yvals, w, b): plt.scatter(xvals, yvals) # plotting dataset plt.xlabel("x values") plt.ylabel("y values") y_start = forward(w, b, xvals.min()) y_end = forward(w, b, xvals.max()) plt.plot([xvals.min(), xvals.max()], [y_start, y_end], c="red") # plotting line from start to end coordinates plt.show() plot_data(xvals, yvals, w, b) def mse_loss(yhat_vals, yvals): return ((yhat_vals - yvals)**2).mean() def w_grad(xvals, yhat_vals, yvals): # Gradient of L wrt w sum_ = np.sum((yhat_vals - yvals)* xvals) return sum_ * (2/len(xvals)) def b_grad(yhat_vals, yvals): # Gradient of L wrt b sum_ = np.sum(yhat_vals - yvals) return sum_ * (2/len(yvals)) lr = 0.0007 # learning rate epochs = 2000 # number of iterations fig, ax = plt.subplots(nrows=4, ncols=3, figsize=(22,18)) ax = ax.flatten() ax_counter = 0 for i in range(epochs): yhat_vals = forward(w,b,xvals) # forward pass through model wgrad = w_grad(xvals, yhat_vals, yvals) # calculating gradient L wrt w bgrad = b_grad(yhat_vals, yvals) # calculating gradient L wrt b w = w - (wgrad*lr) # updating w b = b - (bgrad*lr) # updating b if i % ((epochs // 12)+1) == 0 : # plots every 12 iterations ax[ax_counter].scatter(xvals,yvals) y_start = forward(w, b, xvals.min()) y_end = forward(w, b, xvals.max()) ax[ax_counter].plot([xvals.min(), xvals.max()], [y_start, y_end], c='r', linewidth=3) ax[ax_counter].set_title(f'Iteration : {i}') ax_counter += 1
Այս հոդվածում մենք զրոյից կատարեցինք գծային ռեգրեսիայի իրականացում python-ում: Թեև սա տպավորիչ առաջադրանք է թվում՝ համեմատած այն զարմանալի բաների հետ, որոնք կարելի է անել մեքենայական ուսուցման միջոցով, հասկացությունները, ինչպիսիք են կորստի ֆունկցիաները և ետ տարածումը, հիմք են հանդիսանում ավելի առաջադեմ թեմաների, ինչպիսիք են նեյրոնային ցանցերը և խորը ուսուցումը: