Այս հոդվածում մենք կփորձենք հասկանալ մեքենայական ուսուցման հիմքում ընկած որոշ հիմնական հասկացություններ՝ ձեռքով իրականացնելով գծային ռեգրեսիայի ալգորիթմ:

Գծային ռեգրեսիան ներառում է մի շարք կետերի միջոցով լավագույն համապատասխանության գիծը գտնելը: Այն ի վիճակի է սովորել կապը հատկանիշի կամ մի շարք հատկանիշների և թիրախային փոփոխականի միջև: Օրինակ, այն կարող է օգտագործվել՝ կանխատեսելու այնպիսի հատկանիշների ազդեցությունը, ինչպիսիք են խոնավությունը և ջերմաստիճանը թիրախային փոփոխականի վրա, ինչպիսին տեղումներն են:

Տվյալների հավաքածուի ստեղծում

Գծային ռեգրեսիան պահանջում է պիտակավորված տվյալների բազա, որը պարունակում է 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-ում: Թեև սա տպավորիչ առաջադրանք է թվում՝ համեմատած այն զարմանալի բաների հետ, որոնք կարելի է անել մեքենայական ուսուցման միջոցով, հասկացությունները, ինչպիսիք են կորստի ֆունկցիաները և ետ տարածումը, հիմք են հանդիսանում ավելի առաջադեմ թեմաների, ինչպիսիք են նեյրոնային ցանցերը և խորը ուսուցումը: