За последние несколько месяцев я прошел довольно много курсов по машинному обучению (самых популярных) — «Машинное обучение» Эндрю Нг и первые три курса «Специализация глубокого обучения». Пока я думал, стоит ли переходить на сверточные нейронные сети или нет, я подумал, что было бы лучше дать себе шанс реализовать то, что я узнал до сих пор. Это помогло бы мне укрепить мои основы, и это определенно помогло бы мне в моем карьерном росте.
Теперь я успешно реализовал две модели машинного обучения с нуля — и, как следует из названия, избегая встроенных инструментов, таких как sklearn, tensorflow и т. д. Хотя я не мог отказаться от использования pandas и numpy, я все же убедился, что реализовал функции стоимости и шаги градиентного спуска с нуля. Я задокументировал уроки, полученные в процессе.
Я начну с линейной регрессии в этом блоге.
Набор данных, который я использовал для линейной регрессии, был взят из Kaggle, и вот ссылка на него. Он имеет 300 строк с одной входной переменной и одной выходной переменной. Набор данных казался синтезированным, потому что, увидев его график, мы могли легко сказать, что он линейный.
Внедрение линейной регрессии для этого набора данных показалось довольно простым (и дало некоторую поддержку), поэтому я взялся за это.
Затем я открыл блокнот с этим набором данных в своей учетной записи Kaggle и начал программировать! (Вы также можете загрузить набор данных и кодировать в своих локальных системах с помощью блокнотов Jupyter).
В качестве первого шага я визуализировал набор данных, создав этот график выше. Это было легко, так как был только один вход и один выход. Для нескольких входных данных нам придется использовать парное построение.
После того, как я визуализировал данные, я начал кодировать формулы линейной регрессии на Python. Из моего курса ML я узнал следующее:
Чтобы вычислить стоимость между прогнозируемыми значениями и фактическими значениями (функция стоимости линейной регрессии):
где
m —количество обучающих примеров
J(𝜃₀, 𝜃₁) — функция, вычисляющая потери по всей обучающей выборке
𝜃₀ — Постоянный член в нашем линейном уравнении
𝜃₁ — Коэффициент «x» в линейном уравнении
h₀(x ) —Функция, которая предсказывает значение y при заданном x
y — Фактический результат для заданного x
Здесь 𝜃₀ и 𝜃₁ — это параметры, которые мы должны изучить в нашей модели линейной регрессии.
h₀(x) определяется следующим образом:
Постоянный член 𝜃₀ также называется смещением, а коэффициент при x 𝜃₁ называется весом.
Вот код, который я сделал для того же самого:
#Computes h0(x) #Inputs: x - input data, parameters - θ₀ and θ₁ #Output: h0(x) def compute_hx(x, parameters): m = len(x) # print(parameters[0]) hx = [] # print(“m = “,m) for i in range(m): # print(“i=”,i) prediction = parameters[0] prediction+= parameters[1]*x.iloc[i] hx.append(prediction) return hx #Function to compute Linear Regression Cost #Inputs: hx - The predicted values, y - The actual values #Output: cost, as per the formula def compute_cost(hx, y): m = len(hx) sum1 = 0 for i in range(m): sum1 += (hx[i] — y.iloc[i])**2 cost = sum1/(2*m) return cost
Целью этой задачи линейной регрессии является получение 𝜃₀ и 𝜃₁таких, что h₀(x)иy почти равны (т. е. разница между ними сведена к минимуму). Алгоритм, который помогает минимизировать эту разницу, называется стандартным градиентным спуском.
А алгоритм градиентного спуска (для нашего сценария линейной регрессии) выглядит следующим образом:
Здесь 𝜶 относится к скорости обучения.
Для этого набора данных я решил выполнить пакетный градиентный спуск. Этот алгоритм использует весь обучающий набор для каждой эпохи для вычисления параметров и стоимости.
# This performs one step of the gradient descent loop def gradient_descent_one_step(hx,x,y, parameters, alpha): m = len(hx) sum1 = 0 sum2 = 0 for i in range(m): sum1 += (hx[i] - y.iloc[i]) sum2 += (hx[i] - y.iloc[i])*(x.iloc[i]) cost1 = (sum1 * alpha)/m cost2 = (sum2 * alpha)/m parameters[0] -= cost1 parameters[1] -= cost2 return parameters #Function to perform batch gradient descent, over many epochs def batch_gradient_descent(x, y, parameters, alpha, epochs): cost_list={} parameter_list={} param_cost_list_1={} param_cost_list_0={} for i in range(epochs): hx = compute_hx(x, parameters) cost = compute_cost(hx, y) parameters = gradient_descent_one_step(hx,x,y,parameters,alpha) # The following if block stores data for graph_plotting if i%10==0: # Saving cost and params here, which'll help to plot graphs cost_list[i] = cost parameter_list[i] = parameters param_cost_list_1[parameters[1]] = cost param_cost_list_0[parameters[0]] = cost return parameters,cost,cost_list,parameter_list, param_cost_list_1, param_cost_list_0
С этими функциями мы готовы с кодом, который будет реализовывать модель линейной регрессии. Теперь давайте перейдем к созданию входных данных и кода драйвера для нашей задачи линейной регрессии.
Мой набор данных хранится в формате CSV. Но в то время мне было очень любопытно узнать о форматах данных pickle, и поэтому я сделал обходной путь — сохранил набор данных в файл pickle и прочитал его снова! Однако этот шаг необязателен.
#Code to read data from csv file import pandas as pd dataset = pd.read_pickle('lr_dataset.pkl') print(dataset.shape)
Теперь я перетасую набор данных, чтобы рандомизировать его, и разделю набор данных на обучающий набор, набор для разработки (проверки) и тестовый набор.
#Split the dataset into 70% training, 10% Validation and 20% testing #70% of 300 = 210 #10% of 300 = 30 #20% of 300 = 60 #Before splitting, shuffle the dataset first dataset = dataset.sample(frac = 1).reset_index(drop=True) #Splitting the dataset train_set = dataset[:210].copy() validation_set = dataset[211:241].copy() test_set = dataset[241:].copy() # print(len(train_set)) #Split each of the sets further into x and y x_train = train_set['X'] y_train = train_set['Y'] x_valid = validation_set['X'] y_valid = validation_set['Y'] x_test = test_set['X'] y_test = test_set['Y']
Я не хотел использовать метод sklearn train_test_split, поэтому разделил его таким образом.
Теперь мне нужно было выбрать несколько параметров в качестве входных данных для модели машинного обучения.
- параметры = я выбрал [0,1, -0,1] в начале.
- Скорость обучения = 0,01
- Количество эпох = 100
Я обучил модель следующим кодом:
parameters,cost,cost_list,parameter_list, param_cost_list_1, param_cost_list_0 = batch_gradient_descent(x_train, y_train, parameters, alpha, epoch_max)
После обучения модели я построил график зависимости функции стоимости от номера эпохи.
Эта кривая не дала положительного результата. Стоимость, казалось, увеличивалась после 50 эпох.
После этого я пробовал с разными значениями скорости обучения, начальными значениями параметров и количеством эпох. Я наконец наткнулся на правильный, спустя почти 2 недели:
- параметры = [-0,00001,0,00005]
- Скорость обучения (альфа) = 0,000001
- Количество эпох = 200
С этими значениями я получил лучшую кривую стоимости по сравнению с эпохой:
Я также построил графики, чтобы наблюдать, как веса (𝜃₁ или w) и смещение (𝜃₀ или b) меняются в зависимости от функции стоимости.
В случае таких фреймворков, как sklearn, метод fit() выполняет большую часть описанных выше вычислений, и не нужно будет угадывать эти гиперпараметры. Но следует отметить, что sklearn не использует градиентный спуск для линейной регрессии. Вместо этого используется метод обычных наименьших квадратов.
На графике кривой стоимости и эпохи я выбрал веса соответствующей эпохи, при которой стоимость была наименьшей. Из графика я выбрал значение весов в эпоху 80: [0,0036074754087815596, 0,6810518933205848]
Я использовал эти параметры для вычисления прогнозов для набора для разработки (проверки) и набора тестов. Результаты визуализируются следующим образом:
Примечание. Таким образом я продемонстрировал эффективность модели, поскольку для линейной регрессии невозможно рассчитать точность. Вместо этого используются метрики ошибок, такие как среднеквадратическая ошибка (которую я использовал в этой модели).
Итак, мы наконец-то изучили модель набора данных!
Некоторые будущие улучшения, о которых я думаю:
- Используйте нормальное уравнение вместо градиентного спуска; и посмотрите, насколько различаются конечные параметры.
- Я мог бы автоматизировать ту часть, где я выбираю параметры с наименьшими затратами. Вместо того, чтобы выбирать его вручную, глядя на графики, я мог бы внедрить этот шаг в сам код.
- Используйте ML Frameworks и сравните производительность полученной модели с той, которую я только что построил.
Весь код можно посмотреть здесь.
Спасибо за чтение!