Макалада жөнөкөй (жупташкан) регрессия сызыгынын математикалык теңдемесин аныктоонун бир нече жолдору каралат.
Бул жерде талкууланган теңдемени чечүүнүн бардык ыкмалары эң кичине квадраттар ыкмасына негизделген. Методдорду төмөнкүдөй белгилейли:
- Аналитикалык чечим
- Градиенттин түшүүсү
- Стохастикалык градиенттин түшүүсү
Түз сызыктын теңдемесин чечүүнүн ар бир ыкмасы үчүн макалада ар кандай функциялар каралган, алар негизинен китепкананы колдонбостон жазылгандарга бөлүнөт. numpy жана эсептөөлөр үчүн колдонгондор numpy. Бул билгичтик менен пайдалануу деп эсептелет numpy эсептөө чыгымдарын азайтат.
Макалада берилген бардык код тилде жазылган питон 2.7 менен Jupyter дептери. Булак коду жана үлгү маалыматтары бар файл жайгаштырылат
Макала жаңыдан баштагандарга да, жасалма интеллекттин өтө кеңири бөлүмүн - машина үйрөнүүнү акырындык менен үйрөнө баштагандарга да багытталган.
Материалды түшүндүрүү үчүн биз абдан жөнөкөй мисалды колдонобуз.
Мисал шарттары
Бизде көз карандылыкты мүнөздөгөн беш баалуулук бар Y от X (No1 таблица):
Таблица №1 «Мисал шарттары»
Биз баалуулуктар деп ойлойбуз жылдын айы болуп саналат, жана - ушул айдагы киреше. Башкача айтканда, киреше жылдын айына жараша болот, жана - киреше көз каранды болгон жалгыз белги.
Мисалы, кирешенин жылдын айына шарттуу көз карандылыгы жагынан да, баалуулуктардын саны боюнча да - алардын саны өтө аз. Бирок, мындай жөнөкөйлөштүрүү, алар айткандай, башталгычтар өздөштүргөн материалды ар дайым оңой эмес, түшүндүрүүгө мүмкүндүк берет. Жана ошондой эле сандардын жөнөкөйлүгү олуттуу эмгек чыгымдары жок эле кагаз жүзүндө мисал чечүүнү каалагандарга мүмкүндүк берет.
Мисалда берилген көз карандылыкты форманын жөнөкөй (жупташкан) регрессия сызыгынын математикалык теңдемеси менен абдан жакшы жакындатууга болот деп ойлойлу:
кайда киреше алынган ай, - айга тиешелүү кирешелер; и бааланган линиянын регрессия коэффициенттери болуп саналат.
Коэффициент экенин белгилей кетүү керек көбүнчө болжолдуу сызыктын эңкейиши же градиенти деп аталат; болгон сумманы билдирет ал өзгөргөндө .
Албетте, биздин мисалдагы милдетибиз теңдемеде ушундай коэффициенттерди тандоо и , анда биздин эсептелген кирешелердин ай боюнча туура жооптордон четтөөлөрү, б.а. үлгүдө берилген баалуулуктар минималдуу болот.
Эң кичине квадрат ыкмасы
Эң кичине квадраттар ыкмасына ылайык, четтөөнү квадраттоо жолу менен эсептөө керек. Бул ыкма карама-каршы белгилери бар болсо, четтөөлөрдү өз ара жокко чыгаруудан качууга мүмкүндүк берет. Мисалы, бир учурда, четтөө болуп саналат +5 (плюс беш), жана башка -5 (минус беш), анда четтөөлөрдүн суммасы бири-бирин жокко чыгарат жана 0 (нөл) түзөт. Четтөөнүн квадратын эмес, модулдун касиетин колдонсо болот, ошондо бардык четтөөлөр оң болуп, топтолот. Биз бул пунктка майда-чүйдөсүнө чейин токтолбойбуз, бирок жөн гана эсептөөлөрдүн ыңгайлуулугу үчүн четтөөнүн квадратын алуу салтка айланганын көрсөтөбүз.
Биз квадраттык четтөөлөрдүн (каталардын) эң аз суммасын аныктай турган формула мына ушундай болот:
кайда чыныгы жооптордун жакындоо функциясы (башкача айтканда, биз эсептеген киреше),
туура жооптор (үлгүдө берилген киреше),
үлгү индекси болуп саналат (четтөө аныкталган айдын саны)
Функцияны дифференциалдап, жарым-жартылай дифференциалдык теңдемелерди аныктап, аналитикалык чечимге өтүүгө даяр бололу. Бирок адегенде дифференциация деген эмне жөнүндө кыскача экскурсия жасап, туундунун геометриялык маанисин эстеп көрөлү.
Дифференциация
Дифференциалдоо – функциянын туундусун табуу операциясы.
Туунду эмне үчүн колдонулат? Функциянын туундусу функциянын өзгөрүү ылдамдыгын мүнөздөйт жана анын багытын айтат. Эгерде берилген чекиттеги туунду оң болсо, анда функция өсөт, антпесе функция төмөндөйт. Ал эми абсолюттук туундунун мааниси канчалык чоң болсо, функциянын маанилеринин өзгөрүү ылдамдыгы ошончолук жогору, ошондой эле функциянын графигинин эңкейиши да ошончолук жогору болот.
Мисалы, декарттык координаттар системасынын шартында М(0,0) чекитиндеги туундунун мааниси 25 + мааниси жылдырылып, берилген чекитте дегенди билдирет шарттуу бирдик, маани боюнча оңго 25 шарттуу бирдикке көбөйөт. Графикте бул баалуулуктардын кескин көтөрүлүшү сыяктуу көрүнөт берилген чекиттен.
Башка мисал. Туунду маани бирдей -0,1 которгондо дегенди билдирет бир шарттуу бирдикке, мааниге 0,1 шарттуу бирдикке гана азаят. Ошол эле учурда, функциянын графигинде биз анча байкалбаган ылдый эңкейишти байкай алабыз. Тоо менен салыштыруу, биз мурунку мисалдан айырмаланып, тоодон акырындык менен ылдый түшүп бара жаткандайбыз, ал жерде абдан тик чокуларга чыгууга туура келген :)
Ошентип, функцияны дифференциялоодон кийин ыктымалдыгы боюнча и , 1-даражадагы жарым-жартылай дифференциалдык теңдемелерди аныктайбыз. Теңдемелерди аныктагандан кийин, биз эки теңдеменин тутумун алабыз, аны чечүү менен биз коэффициенттердин ушундай маанилерин тандай алабыз. и , анда берилген чекиттердеги тиешелүү туундулардын маанилери өтө, өтө аз өлчөмдө өзгөрөт, ал эми аналитикалык чечимде такыр өзгөрбөйт. Башкача айтканда, табылган коэффициенттердеги ката функциясы минимумга жетет, анткени бул чекиттердеги жарым-жартылай туундулардын маанилери нөлгө барабар болот.
Ошентип, дифференциялоонун эрежелерине ылайык, коэффициентке карата 1-даражадагы жарым-жартылай туунду теңдеме формада болот:
карата 1-даражадагы жарым-жартылай туунду теңдеме формада болот:
Натыйжада, биз абдан жөнөкөй аналитикалык чечими бар теңдемелердин системасын алдык:
башталышы{теңдеме*}
башталышы{иштер}
na + bsumlimits_{i=1}^nx_i — sumlimits_{i=1}^ny_i = 0
sumlimits_{i=1}^nx_i(a +bsumlimits_{i=1}^nx_i — sumlimits_{i=1}^ny_i) = 0
аягы{иштер}
аягы{теңдеме*}
Теңдемени чечүүдөн мурун, алдын ала жүктөйбүз, жүктөөнүн туура экенин текшерип, маалыматтарды форматтайлы.
Маалыматтарды жүктөө жана форматтоо
Белгилей кетчү нерсе, аналитикалык чечим үчүн, андан кийин градиенттик жана стохастикалык градиенттик түшүү үчүн биз кодду эки вариантта колдонобуз: китепкананы колдонуу numpy жана аны колдонбостон, анда бизге тиешелүү маалыматтарды форматтоо керек болот (кодду караңыз).
Маалыматтарды жүктөө жана иштетүү коду
# импортируем все нужные нам библиотеки
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import math
import pylab as pl
import random
# графики отобразим в Jupyter
%matplotlib inline
# укажем размер графиков
from pylab import rcParams
rcParams['figure.figsize'] = 12, 6
# отключим предупреждения Anaconda
import warnings
warnings.simplefilter('ignore')
# загрузим значения
table_zero = pd.read_csv('data_example.txt', header=0, sep='t')
# посмотрим информацию о таблице и на саму таблицу
print table_zero.info()
print '********************************************'
print table_zero
print '********************************************'
# подготовим данные без использования NumPy
x_us = []
[x_us.append(float(i)) for i in table_zero['x']]
print x_us
print type(x_us)
print '********************************************'
y_us = []
[y_us.append(float(i)) for i in table_zero['y']]
print y_us
print type(y_us)
print '********************************************'
# подготовим данные с использованием NumPy
x_np = table_zero[['x']].values
print x_np
print type(x_np)
print x_np.shape
print '********************************************'
y_np = table_zero[['y']].values
print y_np
print type(y_np)
print y_np.shape
print '********************************************'
Визуализация
Эми, биринчиден, маалыматтарды жүктөгөндөн кийин, экинчиден, жүктөөнүн тууралыгын текшерип, акырында маалыматтарды форматтагандан кийин, биз биринчи визуализацияны жүргүзөбүз. Бул үчүн көбүнчө колдонулган ыкма жуп сюжет китепкана деңиз туулган. Биздин мисалда саны чектелүү болгондуктан китепкананы колдонуунун эч кандай пайдасы жок деңиз туулган. Биз кадимки китепкананы колдонобуз matplotlib жана жөн гана чачыранды карап.
Scatterplot коду
print 'График №1 "Зависимость выручки от месяца года"'
plt.plot(x_us,y_us,'o',color='green',markersize=16)
plt.xlabel('$Months$', size=16)
plt.ylabel('$Sales$', size=16)
plt.show()
№1 диаграмма «Кирешелер жылдын айына көз карандылыгы»
Аналитикалык чечим
Келгиле, эң кеңири таралган куралдарды колдонолу код жана теңдемелер системасын чечиңиз:
башталышы{теңдеме*}
башталышы{иштер}
na + bsumlimits_{i=1}^nx_i — sumlimits_{i=1}^ny_i = 0
sumlimits_{i=1}^nx_i(a +bsumlimits_{i=1}^nx_i — sumlimits_{i=1}^ny_i) = 0
аягы{иштер}
аягы{теңдеме*}
Крамердин эрежеси боюнча аркылуу жалпы аныктагычты, ошондой эле аныктоочуларды табабыз жана тарабынан , андан кийин аныктагычты бөлүү жалпы аныктоочуга - коэффициентти табыңыз , ошентип биз коэффициентти табабыз .
Аналитикалык чечим коду
# определим функцию для расчета коэффициентов a и b по правилу Крамера
def Kramer_method (x,y):
# сумма значений (все месяца)
sx = sum(x)
# сумма истинных ответов (выручка за весь период)
sy = sum(y)
# сумма произведения значений на истинные ответы
list_xy = []
[list_xy.append(x[i]*y[i]) for i in range(len(x))]
sxy = sum(list_xy)
# сумма квадратов значений
list_x_sq = []
[list_x_sq.append(x[i]**2) for i in range(len(x))]
sx_sq = sum(list_x_sq)
# количество значений
n = len(x)
# общий определитель
det = sx_sq*n - sx*sx
# определитель по a
det_a = sx_sq*sy - sx*sxy
# искомый параметр a
a = (det_a / det)
# определитель по b
det_b = sxy*n - sy*sx
# искомый параметр b
b = (det_b / det)
# контрольные значения (прооверка)
check1 = (n*b + a*sx - sy)
check2 = (b*sx + a*sx_sq - sxy)
return [round(a,4), round(b,4)]
# запустим функцию и запишем правильные ответы
ab_us = Kramer_method(x_us,y_us)
a_us = ab_us[0]
b_us = ab_us[1]
print ' 33[1m' + ' 33[4m' + "Оптимальные значения коэффициентов a и b:" + ' 33[0m'
print 'a =', a_us
print 'b =', b_us
print
# определим функцию для подсчета суммы квадратов ошибок
def errors_sq_Kramer_method(answers,x,y):
list_errors_sq = []
for i in range(len(x)):
err = (answers[0] + answers[1]*x[i] - y[i])**2
list_errors_sq.append(err)
return sum(list_errors_sq)
# запустим функцию и запишем значение ошибки
error_sq = errors_sq_Kramer_method(ab_us,x_us,y_us)
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений" + ' 33[0m'
print error_sq
print
# замерим время расчета
# print ' 33[1m' + ' 33[4m' + "Время выполнения расчета суммы квадратов отклонений:" + ' 33[0m'
# % timeit error_sq = errors_sq_Kramer_method(ab,x_us,y_us)
Бул жерде бизде эмне бар:
Ошентип, коэффициенттердин маанилери табылды, квадраттык четтөөлөрдүн суммасы аныкталды. Табылган коэффициенттерге ылайык чачыратуу гистограммасына түз сызык салалы.
Регрессия сызык коду
# определим функцию для формирования массива рассчетных значений выручки
def sales_count(ab,x,y):
line_answers = []
[line_answers.append(ab[0]+ab[1]*x[i]) for i in range(len(x))]
return line_answers
# построим графики
print 'Грфик№2 "Правильные и расчетные ответы"'
plt.plot(x_us,y_us,'o',color='green',markersize=16, label = '$True$ $answers$')
plt.plot(x_us, sales_count(ab_us,x_us,y_us), color='red',lw=4,
label='$Function: a + bx,$ $where$ $a='+str(round(ab_us[0],2))+',$ $b='+str(round(ab_us[1],2))+'$')
plt.xlabel('$Months$', size=16)
plt.ylabel('$Sales$', size=16)
plt.legend(loc=1, prop={'size': 16})
plt.show()
№2 диаграмма «Туура жана эсептелген жооптор»
Ар бир ай үчүн четтөө графигин карай аласыз. Биздин учурда, биз андан эч кандай олуттуу практикалык мааниге ээ болбойбуз, бирок биз жөнөкөй сызыктуу регрессиялык теңдеме кирешенин жылдын айына көз карандылыгын канчалык деңгээлде жакшы мүнөздөй турганына болгон кызыгуубузду канааттандырабыз.
Четтөө диаграммасынын коду
# определим функцию для формирования массива отклонений в процентах
def error_per_month(ab,x,y):
sales_c = sales_count(ab,x,y)
errors_percent = []
for i in range(len(x)):
errors_percent.append(100*(sales_c[i]-y[i])/y[i])
return errors_percent
# построим график
print 'График№3 "Отклонения по-месячно, %"'
plt.gca().bar(x_us, error_per_month(ab_us,x_us,y_us), color='brown')
plt.xlabel('Months', size=16)
plt.ylabel('Calculation error, %', size=16)
plt.show()
Диаграмма № 3 «Четтөөлөр, %»
Кемчиликсиз эмес, бирок биз тапшырмабызды аткардык.
Коэффициенттерди аныктоо үчүн функцияны жазалы и китепкананы колдонот numpy, тагыраак айтканда, биз эки функцияны жазабыз: бири псевдоинверс матрицаны (практикада сунуш кылынбайт, анткени процесс эсептөө татаал жана туруксуз болгондуктан), экинчиси матрицалык теңдеменин жардамы менен.
Аналитикалык чечим коду (NumPy)
# для начала добавим столбец с не изменяющимся значением в 1.
# Данный столбец нужен для того, чтобы не обрабатывать отдельно коэффицент a
vector_1 = np.ones((x_np.shape[0],1))
x_np = table_zero[['x']].values # на всякий случай приведем в первичный формат вектор x_np
x_np = np.hstack((vector_1,x_np))
# проверим то, что все сделали правильно
print vector_1[0:3]
print x_np[0:3]
print '***************************************'
print
# напишем функцию, которая определяет значения коэффициентов a и b с использованием псевдообратной матрицы
def pseudoinverse_matrix(X, y):
# задаем явный формат матрицы признаков
X = np.matrix(X)
# определяем транспонированную матрицу
XT = X.T
# определяем квадратную матрицу
XTX = XT*X
# определяем псевдообратную матрицу
inv = np.linalg.pinv(XTX)
# задаем явный формат матрицы ответов
y = np.matrix(y)
# находим вектор весов
return (inv*XT)*y
# запустим функцию
ab_np = pseudoinverse_matrix(x_np, y_np)
print ab_np
print '***************************************'
print
# напишем функцию, которая использует для решения матричное уравнение
def matrix_equation(X,y):
a = np.dot(X.T, X)
b = np.dot(X.T, y)
return np.linalg.solve(a, b)
# запустим функцию
ab_np = matrix_equation(x_np,y_np)
print ab_np
Коэффициенттерди аныктоого кеткен убакытты салыштырып көрөлү и , берилген 3 ыкмага ылайык.
Эсептөө убактысын эсептөө үчүн код
print ' 33[1m' + ' 33[4m' + "Время выполнения расчета коэффициентов без использования библиотеки NumPy:" + ' 33[0m'
% timeit ab_us = Kramer_method(x_us,y_us)
print '***************************************'
print
print ' 33[1m' + ' 33[4m' + "Время выполнения расчета коэффициентов с использованием псевдообратной матрицы:" + ' 33[0m'
%timeit ab_np = pseudoinverse_matrix(x_np, y_np)
print '***************************************'
print
print ' 33[1m' + ' 33[4m' + "Время выполнения расчета коэффициентов с использованием матричного уравнения:" + ' 33[0m'
%timeit ab_np = matrix_equation(x_np, y_np)
Бир аз сандагы маалыматтар менен алдыда "өз алдынча жазылган" функция чыгат, ал Крамердин ыкмасы менен коэффициенттерди табат.
Эми коэффициенттерди табуунун башка жолдоруна өтсөңүз болот и .
Градиенттин түшүүсү
Биринчиден, градиент деген эмне экенин аныктап алалы. Жөнөкөй сөз менен айтканда, градиент функциянын максималдуу өсүү багытын көрсөткөн сегмент. Тоого чыгууга окшошуп, градиент беттери тоонун чокусуна эң тик чыккан жер. Тоо менен мисалды иштеп чыгуу, биз, чынында, мүмкүн болушунча тезирээк түздүккө жетүү үчүн эң тик түшүү керек экенин эстейбиз, башкача айтканда, минимум - функция көбөйбөгөн же азайбаган жер. Бул учурда туунду нөлгө барабар болот. Демек, бизге градиент эмес, антиградиент керек. Антиградиентти табуу үчүн градиентти көбөйтүш керек -1 (минус бир).
Функциянын бир нече минимумдары болушу мүмкүн экенине көңүл буралы жана төмөндө сунуш кылынган алгоритм боюнча алардын бирине түшкөндөн кийин, табылгандан төмөн болушу мүмкүн болгон башка минимум таба албайбыз. Эс алалы, бул бизге коркунуч эмес! Биздин учурда биз бир минимум менен алектенебиз, анткени биздин функциябыз графикте кадимки парабола. Биздин мектептеги математика курсунан баарыбыз жакшы билишибиз керек, параболанын бир гана минимум бар.
Бизге градиент эмне үчүн керек экенин, ошондой эле градиент сегмент экенин, б.а. координаталары так бирдей болгон вектор экенин билгенден кийин. и градиенттүү түшүүнү ишке ашыра алабыз.
Баштоодон мурун, мен түшүү алгоритми жөнүндө бир нече сүйлөмдөрдү окууну сунуштайм:
- Коэффициенттердин координаталарын псевдококустук түрдө аныктайбыз и . Биздин мисалда биз нөлгө жакын коэффициенттерди аныктайбыз. Бул кеңири таралган практика, бирок ар бир иштин өзүнүн практикасы болушу мүмкүн.
- Координатадан чекиттеги 1-даражадагы жарым-жартылай туундунун маанисин алып салуу . Демек, эгерде туунду оң болсо, анда функция көбөйөт. Демек, туундунун маанисин алып салуу менен биз өсүштүн тескери багытында, башкача айтканда, төмөндөө багытында жылабыз. Эгерде туунду терс болсо, анда бул учурда функция азаят жана туундунун маанисин алып салуу менен биз төмөндөө багытында жылабыз.
- Ушундай эле операцияны координат менен аткарабыз : чекиттеги жарым-жартылай туундунун маанисин алып салуу .
- Минималдуудан секирип, терең космоско учуп кетпеш үчүн кадамдын өлчөмүн түшүү багытында коюу керек. Жалпысынан алганда, эсептөө чыгымдарын азайтуу үчүн кадамды кантип туура коюу керектиги жана түшүү процессинде аны кантип өзгөртүү керектиги жөнүндө толук макала жазсаңыз болот. Бирок азыр биздин алдыбызда бир аз башкача милдет турат жана биз кадамдын өлчөмүн илимий “поке” ыкмасы менен же, алар айткандай, эмпирикалык түрдө белгилейбиз.
- Берилген координаттардан чыккандан кийин и туундулардын маанилерин алып салуу, биз жаңы координаттарды алабыз и . Эсептелген координаттардан кийинки кадамды (кемитүү) жасайбыз. Ошентип, цикл талап кылынган конвергенцияга жеткенге чейин кайра-кайра башталат.
Баары! Эми биз Мариана траншеясынын эң терең капчыгайын издөөгө даярбыз. Келиңиз баштайлы.
Градиенттин түшүү коду
# напишем функцию градиентного спуска без использования библиотеки NumPy.
# Функция на вход принимает диапазоны значений x,y, длину шага (по умолчанию=0,1), допустимую погрешность(tolerance)
def gradient_descent_usual(x_us,y_us,l=0.1,tolerance=0.000000000001):
# сумма значений (все месяца)
sx = sum(x_us)
# сумма истинных ответов (выручка за весь период)
sy = sum(y_us)
# сумма произведения значений на истинные ответы
list_xy = []
[list_xy.append(x_us[i]*y_us[i]) for i in range(len(x_us))]
sxy = sum(list_xy)
# сумма квадратов значений
list_x_sq = []
[list_x_sq.append(x_us[i]**2) for i in range(len(x_us))]
sx_sq = sum(list_x_sq)
# количество значений
num = len(x_us)
# начальные значения коэффициентов, определенные псевдослучайным образом
a = float(random.uniform(-0.5, 0.5))
b = float(random.uniform(-0.5, 0.5))
# создаем массив с ошибками, для старта используем значения 1 и 0
# после завершения спуска стартовые значения удалим
errors = [1,0]
# запускаем цикл спуска
# цикл работает до тех пор, пока отклонение последней ошибки суммы квадратов от предыдущей, не будет меньше tolerance
while abs(errors[-1]-errors[-2]) > tolerance:
a_step = a - l*(num*a + b*sx - sy)/num
b_step = b - l*(a*sx + b*sx_sq - sxy)/num
a = a_step
b = b_step
ab = [a,b]
errors.append(errors_sq_Kramer_method(ab,x_us,y_us))
return (ab),(errors[2:])
# запишем массив значений
list_parametres_gradient_descence = gradient_descent_usual(x_us,y_us,l=0.1,tolerance=0.000000000001)
print ' 33[1m' + ' 33[4m' + "Значения коэффициентов a и b:" + ' 33[0m'
print 'a =', round(list_parametres_gradient_descence[0][0],3)
print 'b =', round(list_parametres_gradient_descence[0][1],3)
print
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений:" + ' 33[0m'
print round(list_parametres_gradient_descence[1][-1],3)
print
print ' 33[1m' + ' 33[4m' + "Количество итераций в градиентном спуске:" + ' 33[0m'
print len(list_parametres_gradient_descence[1])
print
Биз Мариана чуңкурунун түбүнө чейин чөктүк жана ал жерден бирдей коэффициенттин маанисин таптык и , бул так күтүлгөн нерсе.
Келгиле, дагы бир сүңгүп көрөлү, бул жолу гана биздин терең деңиздеги унаабыз башка технологияларга, тактап айтканда китепканага толот. numpy.
Градиенттин түшүү коду (NumPy)
# перед тем определить функцию для градиентного спуска с использованием библиотеки NumPy,
# напишем функцию определения суммы квадратов отклонений также с использованием NumPy
def error_square_numpy(ab,x_np,y_np):
y_pred = np.dot(x_np,ab)
error = y_pred - y_np
return sum((error)**2)
# напишем функцию градиентного спуска с использованием библиотеки NumPy.
# Функция на вход принимает диапазоны значений x,y, длину шага (по умолчанию=0,1), допустимую погрешность(tolerance)
def gradient_descent_numpy(x_np,y_np,l=0.1,tolerance=0.000000000001):
# сумма значений (все месяца)
sx = float(sum(x_np[:,1]))
# сумма истинных ответов (выручка за весь период)
sy = float(sum(y_np))
# сумма произведения значений на истинные ответы
sxy = x_np*y_np
sxy = float(sum(sxy[:,1]))
# сумма квадратов значений
sx_sq = float(sum(x_np[:,1]**2))
# количество значений
num = float(x_np.shape[0])
# начальные значения коэффициентов, определенные псевдослучайным образом
a = float(random.uniform(-0.5, 0.5))
b = float(random.uniform(-0.5, 0.5))
# создаем массив с ошибками, для старта используем значения 1 и 0
# после завершения спуска стартовые значения удалим
errors = [1,0]
# запускаем цикл спуска
# цикл работает до тех пор, пока отклонение последней ошибки суммы квадратов от предыдущей, не будет меньше tolerance
while abs(errors[-1]-errors[-2]) > tolerance:
a_step = a - l*(num*a + b*sx - sy)/num
b_step = b - l*(a*sx + b*sx_sq - sxy)/num
a = a_step
b = b_step
ab = np.array([[a],[b]])
errors.append(error_square_numpy(ab,x_np,y_np))
return (ab),(errors[2:])
# запишем массив значений
list_parametres_gradient_descence = gradient_descent_numpy(x_np,y_np,l=0.1,tolerance=0.000000000001)
print ' 33[1m' + ' 33[4m' + "Значения коэффициентов a и b:" + ' 33[0m'
print 'a =', round(list_parametres_gradient_descence[0][0],3)
print 'b =', round(list_parametres_gradient_descence[0][1],3)
print
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений:" + ' 33[0m'
print round(list_parametres_gradient_descence[1][-1],3)
print
print ' 33[1m' + ' 33[4m' + "Количество итераций в градиентном спуске:" + ' 33[0m'
print len(list_parametres_gradient_descence[1])
print
Коэффициенттин маанилери и өзгөрүлгүс.
Келгиле, градиенттин түшүүсү учурунда ката кандайча өзгөргөнүн, башкача айтканда, ар бир кадам сайын квадраттык четтөөлөрдүн суммасы кандай өзгөргөнүн карап көрөлү.
Квадраттык четтөөлөрдүн суммасын графикке салуу үчүн код
print 'График№4 "Сумма квадратов отклонений по-шагово"'
plt.plot(range(len(list_parametres_gradient_descence[1])), list_parametres_gradient_descence[1], color='red', lw=3)
plt.xlabel('Steps (Iteration)', size=16)
plt.ylabel('Sum of squared deviations', size=16)
plt.show()
График №4 «Градиенттин түшүүдөгү квадраттык четтөөлөрдүн суммасы»
Графикте биз ар бир кадам сайын ката азайып, белгилүү бир сандагы кайталоодон кийин дээрлик горизонталдуу сызыкты байкайбыз.
Акыр-аягы, келгиле, кодду аткаруу убакытындагы айырманы баалайлы:
Градиенттин түшүү убактысын эсептөө үчүн код
print ' 33[1m' + ' 33[4m' + "Время выполнения градиентного спуска без использования библиотеки NumPy:" + ' 33[0m'
%timeit list_parametres_gradient_descence = gradient_descent_usual(x_us,y_us,l=0.1,tolerance=0.000000000001)
print '***************************************'
print
print ' 33[1m' + ' 33[4m' + "Время выполнения градиентного спуска с использованием библиотеки NumPy:" + ' 33[0m'
%timeit list_parametres_gradient_descence = gradient_descent_numpy(x_np,y_np,l=0.1,tolerance=0.000000000001)
Балким, биз туура эмес кылып жаткандырбыз, бирок бул китепкананы колдонбогон жөнөкөй "үйдө жазылган" функция. numpy китепкананы колдонуу менен функциянын эсептөө убактысынан ашып кетет numpy.
Бирок биз бир орунда турбайбыз, бирок жөнөкөй сызыктуу регрессия теңдемесин чечүүнүн дагы бир кызыктуу жолун изилдөөгө карай бара жатабыз. Жолугушуу!
Стохастикалык градиенттин түшүүсү
Стохастикалык градиенттик түшүүнүн иштөө принцибин тез түшүнүү үчүн анын кадимки градиенттүү түшүүдөн айырмасын аныктаган жакшы. Биз, градиенттик ылдыйда, туундуларынын теңдемелеринде и үлгүдөгү бардык мүмкүнчүлүктөрдүн жана чыныгы жооптордун маанилеринин суммасын колдонгон (б.а. бардыгынын суммасы и ). Стохастикалык градиент ылдыйда биз үлгүдөгү бардык баалуулуктарды колдонбойбуз, анын ордуна псевдо-кокустук деп аталган үлгү индексин тандап, анын маанилерин колдонобуз.
Мисалы, индекс 3 (үч) деп аныкталса, анда биз маанилерди алабыз и , анда биз баалуулуктарды туунду теңдемелерге алмаштырып, жаңы координаттарды аныктайбыз. Андан кийин, координаттарды аныктап, биз кайрадан псевдо-кокустуктан тандап индексти аныктайбыз, индекске туура келген маанилерди жарым-жартылай дифференциалдык теңдемелерге алмаштырабыз жана координаталарды жаңы ыкма менен аныктайбыз. и жана башкалар. конвергенция жашылга айланганга чейин. Бир караганда, бул такыр иштебей тургандай сезилиши мүмкүн, бирок ошондой болот. Ырас, ката кадам сайын азайбайт, бирок, албетте, тенденция бар.
Стохастикалык градиенттин кадимки градиентке караганда кандай артыкчылыктары бар? Эгерде биздин үлгүнүн көлөмү абдан чоң болсо жана он миңдеген маанилер менен ченелет, анда бүт тандоого караганда, айталы, алардын кокус миңин иштетүү алда канча оңой. Бул жерде стохастикалык градиенттин түшүүсү пайда болот. Биздин учурда, албетте, биз көп айырмачылыкты байкабайбыз.
Келгиле, кодду карап көрөлү.
Стохастикалык градиенттин түшүү коду
# определим функцию стох.град.шага
def stoch_grad_step_usual(vector_init, x_us, ind, y_us, l):
# выбираем значение икс, которое соответствует случайному значению параметра ind
# (см.ф-цию stoch_grad_descent_usual)
x = x_us[ind]
# рассчитывыаем значение y (выручку), которая соответствует выбранному значению x
y_pred = vector_init[0] + vector_init[1]*x_us[ind]
# вычисляем ошибку расчетной выручки относительно представленной в выборке
error = y_pred - y_us[ind]
# определяем первую координату градиента ab
grad_a = error
# определяем вторую координату ab
grad_b = x_us[ind]*error
# вычисляем новый вектор коэффициентов
vector_new = [vector_init[0]-l*grad_a, vector_init[1]-l*grad_b]
return vector_new
# определим функцию стох.град.спуска
def stoch_grad_descent_usual(x_us, y_us, l=0.1, steps = 800):
# для самого начала работы функции зададим начальные значения коэффициентов
vector_init = [float(random.uniform(-0.5, 0.5)), float(random.uniform(-0.5, 0.5))]
errors = []
# запустим цикл спуска
# цикл расчитан на определенное количество шагов (steps)
for i in range(steps):
ind = random.choice(range(len(x_us)))
new_vector = stoch_grad_step_usual(vector_init, x_us, ind, y_us, l)
vector_init = new_vector
errors.append(errors_sq_Kramer_method(vector_init,x_us,y_us))
return (vector_init),(errors)
# запишем массив значений
list_parametres_stoch_gradient_descence = stoch_grad_descent_usual(x_us, y_us, l=0.1, steps = 800)
print ' 33[1m' + ' 33[4m' + "Значения коэффициентов a и b:" + ' 33[0m'
print 'a =', round(list_parametres_stoch_gradient_descence[0][0],3)
print 'b =', round(list_parametres_stoch_gradient_descence[0][1],3)
print
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений:" + ' 33[0m'
print round(list_parametres_stoch_gradient_descence[1][-1],3)
print
print ' 33[1m' + ' 33[4m' + "Количество итераций в стохастическом градиентном спуске:" + ' 33[0m'
print len(list_parametres_stoch_gradient_descence[1])
Коэффициенттерди кылдаттык менен карап, «Бул кантип болушу мүмкүн?» деген суроону кармайбыз. Бизде башка коэффиценттердин мааниси бар и . Балким, стохастикалык градиенттин түшүүсү теңдеменин оптималдуу параметрлерин тапкандыр? Тилекке каршы жок. Квадраттык четтөөлөрдүн суммасын карап көрүү жана коэффициенттердин жаңы маанилери менен ката чоңураак экенин көрүү жетиштүү. Биз үмүтсүздүккө шашылбайбыз. Келгиле, катаны өзгөртүү графигин түзөлү.
Стохастикалык градиент ылдыйда квадраттык четтөөлөрдүн суммасынын графиктерин түзүү үчүн код
print 'График №5 "Сумма квадратов отклонений по-шагово"'
plt.plot(range(len(list_parametres_stoch_gradient_descence[1])), list_parametres_stoch_gradient_descence[1], color='red', lw=2)
plt.xlabel('Steps (Iteration)', size=16)
plt.ylabel('Sum of squared deviations', size=16)
plt.show()
График №5 «Стохастикалык градиенттин түшүүсүнүн квадраттык четтөөлөрүнүн суммасы»
Графикти карасак баары өз ордуна келип, азыр баарын оңдойбуз.
Анда эмне болду? Төмөнкү окуя болду. Бир айды кокусунан тандап алганыбызда, биздин алгоритм кирешени эсептөөдөгү катаны азайтууга аракет кылат. Андан кийин дагы бир айды тандап, эсепти кайталайбыз, бирок экинчи тандалган ай үчүн катаны азайтабыз. Эми эсиңизде болсун, алгачкы эки ай жөнөкөй сызыктуу регрессия теңдемесинин сызыгынан кыйла четтеп кетет. Бул эки айдын кайсынысы болбосун тандалганда, алардын ар биринин катасын азайтуу менен, биздин алгоритм бардык үлгүдөгү катаны олуттуу түрдө жогорулатат дегенди билдирет. Анда эмне кылуу керек? Жооп жөнөкөй: түшүү кадамын азайтуу керек. Анткени, түшүү кадамын азайтуу менен, ката да өйдө-ылдый "секирүүнү" токтотот. Тескерисинче, "секирүү" катасы токтобойт, бирок аны ушунчалык тез жасабайт :) Келгиле, текшерип көрөлү.
SGDди кичине кадамдар менен иштетүү үчүн код
# запустим функцию, уменьшив шаг в 100 раз и увеличив количество шагов соответсвующе
list_parametres_stoch_gradient_descence = stoch_grad_descent_usual(x_us, y_us, l=0.001, steps = 80000)
print ' 33[1m' + ' 33[4m' + "Значения коэффициентов a и b:" + ' 33[0m'
print 'a =', round(list_parametres_stoch_gradient_descence[0][0],3)
print 'b =', round(list_parametres_stoch_gradient_descence[0][1],3)
print
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений:" + ' 33[0m'
print round(list_parametres_stoch_gradient_descence[1][-1],3)
print
print ' 33[1m' + ' 33[4m' + "Количество итераций в стохастическом градиентном спуске:" + ' 33[0m'
print len(list_parametres_stoch_gradient_descence[1])
print 'График №6 "Сумма квадратов отклонений по-шагово"'
plt.plot(range(len(list_parametres_stoch_gradient_descence[1])), list_parametres_stoch_gradient_descence[1], color='red', lw=2)
plt.xlabel('Steps (Iteration)', size=16)
plt.ylabel('Sum of squared deviations', size=16)
plt.show()
График №6 «Стохастикалык градиенттин түшүүдөгү квадраттык четтөөлөрдүн суммасы (80 миң кадам)»
Коэффициенттер жакшырды, бирок дагы эле идеалдуу эмес. Гипотетикалык жактан, муну ушундай жол менен оңдоого болот. Биз, мисалы, акыркы 1000 итерацияда минималдуу ката кетирилген коэффициенттердин маанилерин тандайбыз. Ырас, бул үчүн биз коэффициенттердин маанилерин да жазып алышыбыз керек. Биз муну кылбайбыз, тескерисинче графикке көңүл бурабыз. Бул жылмакай көрүнөт жана ката бир калыпта азаят окшойт. Чындыгында, бул туура эмес. Келгиле, биринчи 1000 итерацияны карап көрөлү жана аларды акыркысы менен салыштыралы.
SGD диаграммасынын коду (биринчи 1000 кадам)
print 'График №7 "Сумма квадратов отклонений по-шагово. Первые 1000 итераций"'
plt.plot(range(len(list_parametres_stoch_gradient_descence[1][:1000])),
list_parametres_stoch_gradient_descence[1][:1000], color='red', lw=2)
plt.xlabel('Steps (Iteration)', size=16)
plt.ylabel('Sum of squared deviations', size=16)
plt.show()
print 'График №7 "Сумма квадратов отклонений по-шагово. Последние 1000 итераций"'
plt.plot(range(len(list_parametres_stoch_gradient_descence[1][-1000:])),
list_parametres_stoch_gradient_descence[1][-1000:], color='red', lw=2)
plt.xlabel('Steps (Iteration)', size=16)
plt.ylabel('Sum of squared deviations', size=16)
plt.show()
График №7 «СГДнын квадраттык четтөөлөрүнүн суммасы (биринчи 1000 кадам)»
График №8 «СГДнын квадраттык четтөөлөрүнүн суммасы (акыркы 1000 кадам)»
Түшүүнүн эң башында биз катачылыктын бир калыпта жана кескин төмөндөшүн байкайбыз. Акыркы итерацияларда ката 1,475 маанисин айланып, айланып баратканын көрүп жатабыз жана кээ бир учурларда бул оптималдуу мааниге барабар болот, бирок андан кийин ал дагы эле көтөрүлөт... Кайталап айтам, сиз XNUMXтин маанилерин жазсаңыз болот. коэффициенттер и , анан катасы аз болгондорду тандаңыз. Бирок, бизде олуттуу көйгөй бар болчу: оптималдууга жакын маанилерди алуу үчүн 80 миң кадамды жасоого туура келди (кодду караңыз). Бул градиент ылдыйга салыштырмалуу стохастикалык градиенттик түшүү менен эсептөө убактысын үнөмдөө идеясына карама-каршы келет. Эмнени оңдоого жана жакшыртууга болот? Биринчи итерацияларда биз ишеним менен ылдыйлап баратканыбызды байкап калуу кыйын эмес, демек, биринчи итерацияларда чоң кадам таштап, алга карай кадамды азайтышыбыз керек. Бул макалада биз муну кылбайбыз - бул өтө узун. Каалоочулар муну кантип жасаш керек экенин өздөрү ойлонсо болот, бул кыйын эмес :)
Эми китепкананы колдонуу менен стохастикалык градиенттин түшүүсүн жасайлы numpy (жана биз мурда аныктаган таштарга чалынбайлы)
Стохастикалык градиенттин түшүү коду (NumPy)
# для начала напишем функцию градиентного шага
def stoch_grad_step_numpy(vector_init, X, ind, y, l):
x = X[ind]
y_pred = np.dot(x,vector_init)
err = y_pred - y[ind]
grad_a = err
grad_b = x[1]*err
return vector_init - l*np.array([grad_a, grad_b])
# определим функцию стохастического градиентного спуска
def stoch_grad_descent_numpy(X, y, l=0.1, steps = 800):
vector_init = np.array([[np.random.randint(X.shape[0])], [np.random.randint(X.shape[0])]])
errors = []
for i in range(steps):
ind = np.random.randint(X.shape[0])
new_vector = stoch_grad_step_numpy(vector_init, X, ind, y, l)
vector_init = new_vector
errors.append(error_square_numpy(vector_init,X,y))
return (vector_init), (errors)
# запишем массив значений
list_parametres_stoch_gradient_descence = stoch_grad_descent_numpy(x_np, y_np, l=0.001, steps = 80000)
print ' 33[1m' + ' 33[4m' + "Значения коэффициентов a и b:" + ' 33[0m'
print 'a =', round(list_parametres_stoch_gradient_descence[0][0],3)
print 'b =', round(list_parametres_stoch_gradient_descence[0][1],3)
print
print ' 33[1m' + ' 33[4m' + "Сумма квадратов отклонений:" + ' 33[0m'
print round(list_parametres_stoch_gradient_descence[1][-1],3)
print
print ' 33[1m' + ' 33[4m' + "Количество итераций в стохастическом градиентном спуске:" + ' 33[0m'
print len(list_parametres_stoch_gradient_descence[1])
print
Маанилер колдонбостон түшкөндө дээрлик бирдей болуп чыкты numpy. Бирок, бул логикалуу.
Келгиле, стохастикалык градиенттин түшүүсү бизди канча убакытка алып кеткенин билели.
SGD эсептөө убактысын аныктоо үчүн код (80 миң кадам)
print ' 33[1m' + ' 33[4m' +
"Время выполнения стохастического градиентного спуска без использования библиотеки NumPy:"
+ ' 33[0m'
%timeit list_parametres_stoch_gradient_descence = stoch_grad_descent_usual(x_us, y_us, l=0.001, steps = 80000)
print '***************************************'
print
print ' 33[1m' + ' 33[4m' +
"Время выполнения стохастического градиентного спуска с использованием библиотеки NumPy:"
+ ' 33[0m'
%timeit list_parametres_stoch_gradient_descence = stoch_grad_descent_numpy(x_np, y_np, l=0.001, steps = 80000)
Токойго канчалык алыс болсо, булуттар ошончолук караңгылайт: дагы эле «өзү жазылган» формула эң жакшы натыйжаны көрсөтөт. Мунун баары китепкананы колдонуунун мындан да кылдат жолдору болушу керек экенин көрсөтүп турат numpy, бул чынында эле эсептөө операцияларын тездетет. Бул макалада биз алар жөнүндө биле албайбыз. Бош убактыңызда ойлоно турган нерсе болот :)
кошуу
Жыйынтыктоодон мурун, мен урматтуу окурманыбыздан келип чыккан суроого жооп бергим келет. Эмне үчүн, чындыгында, мындай «кыйноолор» ылдыйда, эмне үчүн биздин колубузда ушундай күчтүү жана жөнөкөй аппарат болсо, биз тоону (көбүнчө ылдый) басып өтүшүбүз керек. Бизди ошол замат туура жерге телепортациялаган аналитикалык чечимдин формасы?
Бул суроонун жообу бетинде. Эми биз абдан жөнөкөй мисалды карап чыктык, анда чыныгы жооп бар бир белгиге көз каранды . Жашоодо муну көп байкабайсың, андыктан бизде 2, 30, 50 же андан көп белги бар деп элестетип көрөлү. Келгиле, ар бир атрибут үчүн миңдеген, ал тургай он миңдеген баалуулуктарды кошолу. Бул учурда, аналитикалык чечим сыноого туруштук бере албай, ийгиликсиз болушу мүмкүн. Өз кезегинде, градиенттин түшүүсү жана анын вариациялары акырындык менен, бирок сөзсүз түрдө бизди максатка - функциянын минимумуна жакындатат. Жана ылдамдык жөнүндө кабатыр болбоңуз - биз кадамдын узундугун (б.а. ылдамдыкты) орнотууга жана жөнгө салууга мүмкүндүк берүүчү жолдорду карап чыгабыз.
Ал эми азыр иш жүзүндө кыскача корутунду.
Биринчиден, макалада берилген материал жөнөкөй (жана гана эмес) сызыктуу регрессия теңдемелерин кантип чечүүнү түшүнүүдө "маалымат таануучуларга" жардам берет деп үмүттөнөм.
Экинчиден, теңдемени чечүүнүн бир нече жолдорун карап чыктык. Эми кырдаалга жараша маселени чечүүгө эң ылайыктуусун тандап алсак болот.
Үчүнчүдөн, биз кошумча орнотуулардын күчүн көрдүк, атап айтканда градиенттин түшүү кадамынын узундугу. Бул параметрди этибарга албай коюуга болбойт. Жогоруда белгиленгендей, эсептөөлөргө кеткен чыгымды азайтуу үчүн түшүү учурунда кадамдын узундугун өзгөртүү керек.
Төртүнчүдөн, биздин учурда, "үй-жазылган" функциялары эсептөөлөр үчүн мыкты убакыт натыйжаларын көрсөттү. Бул китепкананын мүмкүнчүлүктөрүн эң профессионалдуу пайдаланбагандыктан улам болсо керек numpy. Бирок, кандай болсо да, төмөнкү тыянак өзүн көрсөтүп турат. Бир жагынан, кээде калыптанып калган ой-пикирлерге шек келтируу керек, ал эми экинчи жагынан, ар дайым эле бардыгын татаалдаштыра бербейт - тескерисинче, кээде көйгөйдү чечүүнүн жөнөкөй жолу натыйжалуураак болот. Ал эми биздин максат жөнөкөй сызыктуу регрессия теңдемесин чечүү үчүн үч ыкманы талдоо болгондуктан, биз үчүн "өзүн-өзү жазылган" функцияларды колдонуу жетиштүү болду.
Адабият (же ушуга окшогон нерсе)
1. Сызыктуу регрессия
2. Эң кичине квадраттар ыкмасы
3. Туунду
4. Градиент
5. Градиенттин түшүүсү
6. NumPy китепканасы