СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Машинное обучение своими руками ч.1. Линейный фильтр по МНК в черновиках Tutorial

Всем привет, друзья, меня зовут Артур.

В этом цикле статей будем писать алгоритмы машинного обучения своими руками с минимальным использованием фреймворков, разве что numpy.

Данные



Давайте посмотрим на типичную выборку Wine Quality Data Set, попробуем предсказать качество белого вина по его хакактеристиками и посмотрим, какой признак влияет больше всего. Вот признаковые описания:

1 — fixed acidity
2 — volatile acidity
3 — citric acid
4 — residual sugar
5 — chlorides
6 — free sulfur dioxide
7 — total sulfur dioxide
8 — density
9 — pH
10 — sulphates
11 — alcohol
Output variable (based on sensory data):
12 — quality (score between 0 and 10)




Математика



Выборка содержит 4898 объектов, каждый из которых описывается вектором из 11 признаков.

Как следует из самого названия, линейный фильтр, построенный по методу наименьших квадратов имеет две отличительные особенности. Во-первых, он строится для отдельного линейного нейрона. Во-вторых, фунция стоимости $\textbf{E}(w)$, используемая для создания этого фильтра, представляет собой по сути сумму квадратов ошибок и определяется формулой:

$\textbf{E}(w) = \frac{1}{2}\sum_{i=1}^{n}e^2(i),$


где

$e(i) = y(i) - d(i)$


$y(i) = \sum_{k=1}^{m}w_k(i)x_k(i), $



Выборку определим как: $T : \{\textbf{x}(i), d(i); i = 1, 2, ..., n, ...\}$.

То есть:

$\textbf{x}(i) = [x_1(i), x_2(i), ..., x_m(i)]^T \\ \textbf{w}(i) = [w_1(i), w_2(i), ..., w_m(i)]^T \\ y(i) = \textbf{x}^T(i)\textbf{w}(i)$



Запишем соотношение:

$\textbf{e}(n) = \textbf{d}(n) - [\textbf{x}(1), \textbf{x}(2), ..., \textbf{x}(n)]^T\textbf{w}(n) = \textbf{d}(n) - \textbf{X}(n)\textbf{w}(n), (1)$



где $\textbf{d}(n) - $ вектор ожидаемого отклика размерности n:

$\textbf{d}(n) = [d(1), d(2), ..., d(n)]^T.$



$\textbf{X}(n) - $ матрица данных размерности $n \times m.$

Дифференцируя $(1)$ по $\textbf{w}(n),$ получим матрицу градиента:

$\bigtriangledown\textbf{e}(n) = -\textbf{X}(n)$



Следовательно, Якобин $\textbf{e}(n)$ запишем в виде $\textbf{J}(n) = -\textbf{X}(n)$

Опуская некоторые выкладки, замечу, что метод Гаусса-Ньютона, применяемый для минимизации функции стоимости(выше) сходится за одну итерацию. Конечный вид:

$\textbf{w}(n + 1) = \textbf{w}(n) + (\textbf{X}^T\textbf{X}(n))^{-1}\textbf{X}^T(n)(\textbf{d}(n) - \textbf{X}(n)\textbf{w}(n)) = \\(\textbf{X}^T\textbf{X}(n))^{-1}\textbf{X}^T(n)\textbf{d}(n).$



Это выражение можно вербально описать: «Вектор весовых коэффициентов $\textbf{w}(n)$ является решением линейной задачи фильтрации, решаемой по методу наименьших квадратов на интервале наблюдения длительности $n$»

Моделирование



import pandas as pd
import numpy as np
import sklearn


def data_split(X, y, partition):

    lenTrain = X.shape[0] - int(X.shape[0] * partition)

    trainX, trainY, testX, testY = X[:lenTrain], y[:lenTrain], X[lenTrain:], y[lenTrain:]

    return trainX, trainY, testX, testY

def main():

    # Загрузка данных
    ds = pd.read_csv('./winequality-white.csv', sep=';')

    y = ds['quality'].values.reshape((4898, 1))

    # Удаляем вектор желаемых откликов из матрицы признаков
    del ds['quality']

    X = ds

    trainX, trainY, testX, testY = data_split(np.array(X), np.array(y), 0.8)

    """
    >>> print(X.shape)
        (4898, 11)
    
    >>> print(y.shape)
        (4898, 1)
    """

    W = np.dot(np.dot(np.linalg.inv(np.dot(trainX.T, trainX)), trainX.T), trainY)

    """ print(W)

    [[-5.05906229e-02]
     [-1.95851023e+00]
     [-2.93492412e-02]
     [ 2.49883984e-02]
     [-9.42582369e-01]
     [ 4.79078658e-03]
     [-8.77630817e-04]
     [ 2.04204607e+00]
     [ 1.68395142e-01]
     [ 4.16453560e-01]
     [ 3.65633380e-01]]
    """

    MSE_tr = ((np.dot(trainX, W) - trainY) ** 2).mean()
    MSE_te = ((np.dot(testX, W) - testY) ** 2).mean()
    MSE_orig = ((np.dot(X, W) - y) ** 2).mean()

    print(MSE_tr)   # 0.6321616743688561
    print(MSE_te)   # 0.6096450872547713
    print(MSE_orig) # 0.6141502435168787

if __name__ == "__main__":
    main()

комментарии (1)

+1
lair ,  

Ну вы бы хоть объяснили, что такое линейный фильтр, и какое отношение он имеет к задаче предсказания.