Нейронная сеть серии основных алгоритмов машинного обучения

машинное обучение искусственный интеллект алгоритм Нейронные сети
Нейронная сеть серии основных алгоритмов машинного обучения

Впереди написано: я объясню часто используемые алгоритмы машинного обучения с точки зрения новичка (уровень). Мой собственный уровень действительно ограничен, и если есть какие-то ошибки, я надеюсь, что вы укажете на них и не будете вводить вас в заблуждение.

В этой серии уже есть две предыдущие работы (одна отскочилаДрево решений???), заинтересованным читателям предлагается прочитать:

экспериментальный код-Справочная литература-[Подробности см. в блоге в конце]

0 Основное введение

0.1 Почему генерируются нейронные сети

Что касается истории, вы можете проверить ее в Интернете, Вот некоторые из моих взглядов на неизбежность нейронных сетей. Возвращаясь к предыдущей статье, мы видим, что если мы хотим найти простую границу объекта, которая может быть описана существующими функциональными правилами (такими как линия, окружность, эллипс, сфера...), мы можем напрямую выбрать количество его объектов, Потом обучить эту модель, то есть для определенной задачи есть конкретная ее модель, а если чуть посложнее? Как мы можем это сделать? Раньше, когда мы ничего не знали о машинном обучении, в начальной линейной регрессии мы хотели иметь такой черный ящик, вводить в него данные, он может вернуть мне результаты, мне не нужно знать этот черный ящик Внутренняя структура, то есть мне не нужно составлять конкретный план того, как построить количество признаков, а просто напрямую вводить необработанные данные, и выходной результат соответствует нашим ожиданиям, а это означает, что модель может быть использована нами. Итак, вопрос в том, как оправдались наши ожидания? Я тоже не знаю, но я знаю, что если человеку дать картинку и он может определить, кошка это или собака, то разве мыслительный процесс этого человека не похож на черный ящик? Хотя механизм работы внутри точно определен. Тогда можем ли мы имитировать этот процесс и обрабатывать задачи распознавания вот так через этот конвейер, и результат должен быть тем, что мы хотим? Что касается точности, то в этом разница между детьми и взрослыми. Ключевой шаг в развитии человека.

0.2 Что такое нейронная сеть

Я еще мало что знаю, так что вот что говорят другие:Как просто и интересно объяснить, что такое нейронная сеть?

0.3 Какие проблемы могут решить нейронные сети

То же

1 Введение в нейронные сети

Мы уже проанализировали ограничения того, с чем мы можем иметь дело при переходе от линейной регрессии к логистической регрессии, Лично я склонен думать, что линейная регрессия - это скорее прогноз непрерывного значения, а логистическая регрессия - это скорее линейно разделимое разделение типов. Для нелинейных сепарабельных задач приходится прибегать к другим средствам.

1.1 Взгляд на нейронные сети с точки зрения логических операций

Многие, многие основные примеры учений начинаются с логических операций. Я считаю, что после прочтения некоторых предыдущих вводных статей о нейронных сетях у вас должно сложиться определенное представление о нейронных сетях.

Выше приведен график И или НЕ, где красные кружки представляют положительные примеры (1), а черные треугольники представляют отрицательные примеры (0). Давайте превратим это в график нейронной сети.

Согласно таблице истинности нейронная сеть, построенная по вышеуказанным весам, способна реализовать логическую операцию И-ИЛИ. На самом деле видно, что простейший объект обработки нейронной сети может быть линейно разделим, то есть просто делать отображение (активацию).

Давайте посмотрим, как обрабатывается операция XOR:

Наблюдая, вы обнаружите, что граф линейно неразделим, то есть нейронная сеть, которую мы хотим нарисовать, рисуя предыдущую операцию, аналогичную И или НЕ, не может быть нарисована сейчас. Поскольку это распределение не является линейно разделимым, нет никакого способа передатьWТрансформерXстановится монотонной, поэтому только один проход через монотонную функциюfАктивация класса не позволяет достичь разделения.

Но мы можем изменить свое мышление.Поскольку И или не является линейно разделимым, то мы изо всех сил стараемся преобразовать текущую операцию исключающее ИЛИ в И или НЕ, чтобы решить ее.

A\ XOR\ B=(A\land \lnot B) \lor (\lnot A\land B)

С приведенной выше формулой это легко сделать, мы непосредственно передаемXПостроить два нейрона(A\land \lnot B), (\lnot A\land B), а затем активировать эти два нейрона, а затем выполнить операцию ИЛИ над этими двумя нейронами, и результат, полученный после активации, будетA\ XOR\ B.

Обратите внимание, что здесь мы не ограничиваем положения индексов и стандартизируем их в следующем разделе.

Так как кажется, что наша нейронная сеть может выполнять операцию XOR, давайте напишем кусок кода, чтобы проверить это:

# coding: utf-8

import matplotlib.pyplot as plt
import numpy as np

x1 = np.asarray([0, 0, 1, 1])
x2 = np.asarray([0, 1, 0, 1])
X = np.row_stack((np.ones(shape=(1, 4)), x1, x2))
print("X:\n%s" % X)
y = np.asarray([0, 1, 1, 0])
W1 = np.asarray([[-1, 2, -2],
                 [-1, -2, 2]])
W2 = np.asarray([-1, 2, 2])


def sigmoid(input):
    return 1 / (1 + np.power(np.e, -10 * (input)))


np.set_printoptions(precision=6, suppress=True)
z1 = np.matmul(W1, X)
print("W1*X = z1:\n%s" % z1)
a1 = np.row_stack((np.ones(shape=(1, 4)), sigmoid(z1)))
print("sigmoid(z1) = a1:\n%s" % a1)
z2 = np.matmul(W2, a1)
print("W2*a1 = z2:\n%s" % z2)
a2 = sigmoid(z2)
print("------------------------")
print("prediction: %s" % a2)
print("target: %s" % y)
print("------------------------")

# output:
# X:
# [[1. 1. 1. 1.]
#  [0. 0. 1. 1.]
#  [0. 1. 0. 1.]]
# W1*X = z1:
# [[-1. -3.  1. -1.]
#  [-1.  1. -3. -1.]]
# sigmoid(z1) = a1:
# [[1.       1.       1.       1.      ]
#  [0.000045 0.       0.999955 0.000045]
#  [0.000045 0.999955 0.       0.000045]]
# W2*a1 = z2:
# [-0.999818  0.999909  0.999909 -0.999818]
# ------------------------
# prediction: [0.000045 0.999955 0.999955 0.000045]
# target: [0 1 1 0]
# ------------------------

Видно, что прогнозируемое значение соответствует целевому значению. Так что же здесь происходит?

В начале наши особенности[x1; x2]. Потом пройти первый слой, после активацииa1это наша трансформированная функция[a_1^{(1)}; a_2^{(1)}]=sigmoid(W_1X)Мы видели ранее[x1; x2]График линейно неразделим, то[a_1^{(1)}; a_2^{(1)}]Является ли он линейно разделимым как функция? Посмотрите на данные и графики.

# sigmoid(z1) = a1:
# [[1.       1.       1.       1.      ]
#  [0.000045 0.       0.999955 0.000045]
#  [0.000045 0.999955 0.       0.000045]]
# target: [0 1 1 0]

Хм? Линейно сепарабельно! Затем непосредственно выполните метод, аналогичный линейной регрессии, чтобы получить результаты напрямую, и нет необходимости искать другие функции преобразования.

Так что магия здесь в матрицеW_1, он может преобразовать исходный признак в другой тип признака.Здесь нужно глубоко разбираться в матрице.Я в этом не разбираюсь,поэтому не буду нести чушь. . . может видеть[Официальный двуязычный/сборник] Сущность линейной алгебры - сборник серийPS: Честно говоря, я еще немного давным-давно читал, кажется, мне нужно какое-то время, чтобы прочитать это внимательно, ведь в машинном обучении полно матриц.

Следовательно, посредством преобразования признаков линейно неразделимые задачи могут быть линейно разделимы по другим признакам. Затем данные нормализуются при следующем вводе с помощью функции активации, что является магией нейронных сетей.

2 Обучите нейронную сеть

Для того, чтобы понять «мощь» нейросети, мы прямо далиW. И наши обучающие данные - найти подходящиеWЧтобы делать прогнозы, мы будем анализировать нейронную сеть одну за другой в соответствии с методом анализа машинного обучения предыдущей логистической регрессии.

2.1 Функция потерь

Здесь мы можем полностью определить эту модель как черный ящик, Пока выходные данные соответствуют нашему целевому значению, функция потерь здесь в основном такая же, как логистическая регрессия.

Понимание можно понимать как черный ящик, а вычисление — нет, потому что каждый параметр должен обеспечивать точный процесс вычисления. Давайте начнем:

Сначала введите имя: обычно верхний регистр представляет матрицу, верхний индекс представляет количество слоев, нижний регистр представляет только вектор-столбец с верхним индексом, а если нижний индекс заполнен, он представляет определенное значение.

W^{(i)}: первоеiвесовая матрица

W^{(1)} =  \begin{bmatrix} w_{1,0}^{(1)} & w_{1,1}^{(1)} & \ldots \\\\ w_{2,0}^{(1)} & w_{2,1}^{(1)} & \ldots \\\\ \vdots & \vdots & \ddots  \end{bmatrix} bias^{(i)} =  \begin{bmatrix} w_{1,0}^{(i)} \\\\ w_{2,0}^{(i)} \\\\ \vdots \end{bmatrix}

X^{(0)}: введите и добавьте константу для1нейронов, каждый столбец представляет набор данных

X^{(0)} =  \begin{bmatrix} 1 & 1 & 1 & \ldots \\\\ x_{1}^{(1)} & x_{1}^{(2)} & x_{1}^{(3)} &\ldots \\\\ x_{2}^{(1)} & x_{2}^{(2)} & x_{2}^{(3)} & \ldots \\\\ \vdots & \vdots & \vdots & \ddots  \end{bmatrix} x^{(i)} =  \begin{bmatrix} 1 \\\\ x_{1}^{(i)} \\\\ x_{2}^{(i)} \\\\ \vdots  \end{bmatrix}

z^{(i)}: обучающее значение, вы можете поставитьxрассматривается какa^{(0)}

W^{(1)}x = \begin{bmatrix} w_{1,0}^{(1)} & w_{1,1}^{(1)} & w_{1,2}^{(1)} \\\\ w_{2,0}^{(1)} & w_{2,1}^{(1)} & w_{2,2}^{(1)} \\\\ \end{bmatrix} \begin{bmatrix} 1 \\\\ x_{1} \\\\ x_{2} \end{bmatrix}  = \begin{bmatrix} z_{1}^{(1)} \\\\ z_{2}^{(1)} \end{bmatrix}  = z^{(1)} \\\\ W^{(i + 1)}a^{(i)} = \begin{bmatrix} w_{1,0}^{(i + 1)} & w_{1,1}^{(i + 1)} & w_{1,2}^{(i + 1)} \\\\ w_{2,0}^{(i + 1)} & w_{2,1}^{(i + 1)} & w_{2,2}^{(i + 1)} \\\\ \end{bmatrix} \begin{bmatrix} 1 \\\\ a_{1}^{(i)} \\\\ a_{2}^{(i)} \end{bmatrix}  = \begin{bmatrix} z_{1}^{(i + 1)} \\\\ z_{2}^{(i + 1)} \end{bmatrix}  = z^{(i + 1)}

a^{(i)}: значение активации, обратите внимание, что функция активации здесь используетf=sigmoid

a^{(i)} =  \begin{bmatrix} 1 \\\\ a_{1}^{(i)} \\\\ a_{2}^{(i)} \end{bmatrix}  = \begin{bmatrix} 1 \\\\ f(z_{1}^{(i)}) \\\\ f(z_{2}^{(i)}) \end{bmatrix}

Тогда в предыдущем примере XOR мы можем получить:

\begin{aligned} z^{(1)} &= W^{(1)}x \\\\ a^{(1)} &= f(z^{(1)})\ (add\ a_{0}^{(i)}=1) \\\\ z^{(2)} &= W^{(2)}a^{(1)} \\\\ a^{(2)} &= f(z^{(2)}) \\\\ \end{aligned}

функция потерьJ:

J(W) = - y * log(a^{(2)}) - (1 - y) * log(1 - a^{(2)})

мы для входаx_1, x_2Стоимость потерьJ(W). обратите внимание здесьWрассматривается как черный ящикW, здесь для простоты используются только одномерные данные

2.2 Обновление параметров: алгоритм обратного распространения

Если вы никогда раньше не касались алгоритма обратного распространения, подумайте об этом так: наша цель здесь — обновитьW^{(i)}, а алгоритм оптимизации, с которым мы сейчас знакомы, это только алгоритм градиентного спуска (PS: кажется, нам придется снова пересмотреть численный расчет ???), то естественно есть:

W^{(i)} = W^{(i)} - \alpha\frac{\partial\ J}{\partial\ W^{(i)}}

Так

W^{(2)} = W^{(2)} - \alpha\frac{\partial\ J}{\partial\ W^{(2)}} \\\\ W^{(1)} = W^{(1)} - \alpha\frac{\partial\ J}{\partial\ W^{(1)}}

Далее я буду использовать большую волну знаний о выводе матриц.Здесь я готовился дольше всего, потому что для написания кода необходимо знать каждую деталь.Поскольку здесь основное внимание уделяется машинному обучению, я приведу процесс вывода вычислений ., а принцип построения матриц смотрите в справочных материалах.

Matrix calculus: в основном для пониманияNumerator-layout notation,Denominator-layout notationметод представления иScalar-by-matrix identities, другие также рекомендуется посмотреть.Вывод матрицы (часть 1)Вывод матрицы (часть 2)Ознакомьтесь с принципами и практиками. потомМатематические принципы обратного распространения нейронной сетиВы также можете посмотреть.Логическая часть рассуждений может быть не очень строгой, но общая идея очень подходит для чтения.Для нас нам может не понадобиться такой тщательный процесс логических рассуждений, но я перечислю процесс расчета ниже. Заинтересованным можно прочитать.

Что ж, здесь мы находимся, когда у каждого есть определенная основа для руководства. здесь\frac{\partial\ J}{\partial\ W^{(i)}}очевидноscalar-by-matrixЗатем мы используем форму трассировки для решения.

\frac{\partial\ J}{\partial\ W^{(i)}} = \frac{\partial\ J}{\partial\ z^{(i)}}\frac{\partial\ z^{(i)}}{\partial\ W^{(i)}} \\\\

в

\frac{\partial\ z^{(i)}}{\partial\ W^{(i)}} = {a^{(i - 1)}}^T \\\\ \frac{\partial\ J}{\partial\ z^{(i)}} = \frac{\partial\ J}{\partial\ z^{(i + 1)}}\frac{\partial\ z^{(i + 1)}}{\partial\ a^{(i)}}\frac{\partial\ a^{(i)}}{\partial\ z^{(i)}} = {W^{(i + 1)}}^T\frac{\partial\ J}{\partial\ z^{(i + 1)}}.*f'(z^{(i)})

Если вы уже знаете вычисление или считаете, что оно не нужно, вы можете просто пропустить процесс доказательства.

доказывать: известный

\sum_{i,\ j}A_{ij}B_{ij} = tr(A^TB) \\\\ df = \sum_{i,\ j}\frac{\partial f}{\partial X_{ij}}dX_{ij} = tr(\frac{\partial f}{\partial X}^TdX) \\\\ tr(AB) = tr(BA) \\\\ tr(A^T(B .* C)) = tr((A .* B)^TC) \\\\ df(X) = f'(X) .* X

Если вас интересует доказательство приведенной выше формулы, вы можете проверить его в Интернете, потому что... потому что я только чувствую, что формула верна (используя тестовые примеры), и я не очень хорош в строгом доказательстве.

тогда даz^{(i)} = W^{(i)}a^{(i - 1)}дифференциал:

\begin{aligned} dz^{(i)} & = dW^{(i)}a^{(i - 1)} \\\\ & = tr(a^{(i - 1)}dW^{(i)}) \\\\ & = tr(({a^{(i - 1)}}^T)^TdW^{(i)}) \\\\ & \Rightarrow \frac{\partial\ z^{(i)}}{\partial\ W^{(i)}} = {a^{(i - 1)}}^T \\\\ \end{aligned}

Очень просто, первое доказательство завершено, затем доказывается второе:

\begin{aligned} dJ & = tr({\frac{\partial\ J}{\partial\ z^{(i + 1)}}}^Tdz^{(i + 1)}) \\\\ & = tr({\frac{\partial\ J}{\partial\ z^{(i + 1)}}}^TW^{(i + 1)}da^{(i)}) \\\\ & = tr({\frac{\partial\ J}{\partial\ z^{(i + 1)}}}^TW^{(i + 1)}f'(z^{(i)}) .* dz^{(i)}) \\\\ & = tr([{W^{(i + 1)}}^T{\frac{\partial\ J}{\partial\ z^{(i + 1)}}}]^Tf'(z^{(i)}) .* dz^{(i)})  \\\\ & = tr([{W^{(i + 1)}}^T{\frac{\partial\ J}{\partial\ z^{(i + 1)}}} .* f'(z^{(i)})]^T dz^{(i)})  \\\\ & \Rightarrow \frac{\partial\ J}{\partial\ z^{(i)}} = {W^{(i + 1)}}^T{\frac{\partial\ J}{\partial\ z^{(i + 1)}}} .* f'(z^{(i)}) \end{aligned}

Это все для процесса доказательства. из которых только\frac{\partial\ J}{\partial\ z^{(i + 1)}}неизвестно, это авторJопределяется определением.

Из предыдущего анализа мы используем оценку максимального правдоподобия для вычисления ошибки, поэтому:

\begin{aligned} \frac{\partial\ J}{\partial\ z^{(i + 1)}} & = \frac{\partial\ J}{\partial\ a^{(i + 1)}}\frac{\partial\ a^{(i + 1)}}{\partial\ z^{(i + 1)}} \\\\ & = \frac{\partial\ J}{\partial\ a^{(i + 1)}}f'(z^{(i + 1)}) \\\\ & = \frac{\partial\ (- y * log(a^{(i + 1)}) - (1 - y) * log(1 - a^{(i + 1)}))}{\partial\ a^{(i + 1)}}f'(z^{(i + 1)}) \\\\ & = \frac{k(1 - y(1+e^{-kz^{(i + 1)}}))}{e^{-kz^{(i + 1)}}} \end{aligned}

вf(x) = \frac{1}{1 + e^{-kx}}, при отладке кода можно поставитьkЗначение корректируется, чтобы быть соответствующим образом большим, так чтоsigmoidФункция больше похожа на ступенчатую, а значение после активации получается в0Чем больше пролет в ближайшем окружении, тем лучше тренировочный эффект (положительные примеры ближе1, контрпример ближе0). Тогда рассуждения здесь ленивы и напрямую используют «Принцип совместимости измерений» из предыдущего сообщения в блоге, потому что это кажется относительно простым.

3 реализация кода

Здесь я буду реализовывать и использовать рукописный кодTensorFlowСделайте это двумя способами.

3.1 Реализация рукописного кода

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

  • Основная цель – обучение:
def train():
    np.set_printoptions(precision=4, suppress=True)
    x1 = np.asarray([0, 0, 1, 1])
    x2 = np.asarray([0, 1, 0, 1])
    X = np.row_stack((x1, x2))
    y = np.asarray([0, 1, 1, 0])
    shape = [2, 2, 1]
    Learning_Rate = 0.1
    Training_Times = 4000
    W = gradientDescent(X, y, shape, learningrate=Learning_Rate, trainingtimes=Training_Times)

Вам не нужно беспокоиться о параметрах, просто передайте все, что хотите. Настройки параметров не должны сейчас слишком беспокоить, ключевым моментом является использованиеshapeЧтобы контролировать форму нейронной сети, это несколько похоже на предыдущую логистическую регрессию для определения подгоночной функции.

  • Тогда есть конкретная реализация градиентного спуска:
def gradientDescent(X, y, shape, learningrate=0.001, trainingtimes=500):
    W, z, a = [], [], []
    for layer in range(len(shape) - 1):
        row = shape[layer + 1]
        col = shape[layer] + 1
        W.append(np.random.normal(0, 1, row * col).reshape(row, col))
    for i in range(trainingtimes):
        for x, j in zip(X.T, range(len(X[0]))):
            z, a = forward(W, np.asarray([x]).T)
            W = backward(y[j], W, z, a, learningrate)
    return W

Главное инициализироватьW, затем тренируйтесь на данных, распространяйте вперед, затем распространяйте корневое обновление обратноW

  • тогда определениеsigmoidи функция активации с последующим прямым распространением:
k = 2

def sigmoid(x):
    return 1 / (1 + np.power(np.e, -k * (x)))

def actication(data):
    return sigmoid(data)

def forward(W, data):
    z, a = [], []
    a.append(data)
    data = np.row_stack(([1], data))
    for w in W:
        z.append(np.matmul(w, data))
        a.append(actication(z[-1]))
        data = np.row_stack(([1], a[-1]))
    return z, a

Здесь нет никакой математики, все просто.

  • Наконец, суть обратного распространения здесь, и все наши предыдущие результаты расчета применяются здесь:
def backward(y, W, z, a, learningrate):
    length = len(z) + 1
    Jtoz = k * (1 - y * (1 + np.power(np.e, -(k * z[-1])))) / np.power(np.e, -(k * z[-1]))
    # print("loss = %s" % (-y * np.log(a[-1]) - (1 - y) * np.log(1 - a[-1])))
    for layer in range(length - 1, 0, -1):
        i = layer - length
        if (i != -1):
            Jtoz = np.matmul(W[i + 1][:, 1:].T, Jtoz) * k * np.power(np.e, -(k * z[i])) / np.power(
                1 + np.power(np.e, -(k * z[i])), 2)
        W[i] = W[i] - learningrate * np.matmul(Jtoz, np.row_stack(([1], a[i - 1])).T)
    return W

После того, как код написан, давайте протестируем его:

def train():
    np.set_printoptions(precision=4, suppress=True)
    x1 = np.asarray([0, 0, 1, 1])
    x2 = np.asarray([0, 1, 0, 1])
    X = np.row_stack((x1, x2))
    y = np.asarray([0, 1, 1, 0])
    shape = [2, 2, 1]
    Learning_Rate = 0.1
    Training_Times = 4000
    W = gradientDescent(X, y, shape, learningrate=Learning_Rate, trainingtimes=Training_Times)

    print(W)
    testData = np.row_stack((np.ones(shape=(1, 4)), X))
    for w in W:
        testData = np.matmul(w, testData)
        testData = np.row_stack((np.ones(shape=(1, 4)), actication(testData)))
    print(testData[1])

Взгляните на вывод:

# output1:
# [array([[-8.3273,  5.5208,  5.4758],
#        [ 2.7417, -5.944 , -5.9745]]), array([[ 18.5644, -22.4426, -22.4217]])]
# [0.0005 1.     1.     0.0005]
# [array([[ 3.0903, -6.3961,  6.928 ],
#        [ 3.0355,  6.7901, -6.2563]]), array([[ 41.2259, -22.2455, -22.0939]])]
# [0.0024 1.     1.     0.0021]
# [array([[ 5.3893,  4.913 , -7.0756],
#        [ 6.2289, -1.3519, -4.7387]]), array([[  9.8004, -20.0023,  10.2014]])]
# [0.5    1.     0.4995 0.0002]

Иногда результаты тренировок очень хорошие, иногда результаты неудовлетворительны, когда я ставлюWВсе становится лучше, когда начальное значение не генерируется случайным образом:

    W.append(np.asarray([[-1, 1, -1], [-1, -1, 1]]))
    W.append(np.asarray([[-1, 1, 1]]))

# output2:
# [array([[-2.8868,  5.6614, -5.9766],
#        [-2.9168, -5.9789,  5.6363]]), array([[-2.1866, 21.2065, 21.1815]])]
# [0.016  1.     1.     0.0142]
# [array([[-2.9942,  5.7925, -6.0901],
#        [-3.0228, -6.0924,  5.7687]]), array([[-3.6425, 22.3914, 22.3658]])]
# [0.0009 1.     1.     0.0008]

Что касается того, почему это происходит, поскольку мои знания все еще невелики, я могу только догадываться, что алгоритм градиентного спуска, о котором я упоминал ранее, на самом деле зависит от начального значения.Если начальное значение далеко от глобального оптимума, оно не только будет медленно сходиться, но также быть очень медленным, может сходиться к локальному оптимуму. Во-вторых, мы устанавливаем функцию активацииkЗначение не может быть слишком большим, иначе оно будет увеличиваться экспоненциально и влиять на точность, а посколькуkзначение относительно невелико, поэтому в[-1, 1]Интервал не похож на нашу идеальную ступенчатую функцию, поэтому данные сконцентрированы в ограниченном числе тренировочных0.5а не рассеянный[0, 1]в конечной точке интервала. Конечно, это всего лишь предположение, если кто-то знает конкретную причину, сообщите, пожалуйста.

3.2 Реализация TensorFlow

# coding: utf-8

import numpy as np
import tensorflow as tf


def sigmoid(x):
    return 1 / (1 + np.power(np.e, -2 * (x)))


def add_layer(inputs, in_size, out_size, activation_function=None, ):
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs


if __name__ == "__main__":
    x1 = np.asarray([0, 0, 1, 1])
    x2 = np.asarray([0, 1, 0, 1])
    X = np.row_stack((x1, x2))
    y = np.asarray([0, 1, 1, 0]).reshape(1, 4)
    data_X = tf.placeholder(tf.float32, [None, 2])
    data_y = tf.placeholder(tf.float32, [None, 1])

    layer_one = add_layer(data_X, 2, 2, activation_function=sigmoid)
    prediction = add_layer(layer_one, 2, 1, activation_function=sigmoid)

    loss = tf.reduce_mean(tf.reduce_sum(- data_y * tf.log(prediction) - (1 - data_y) * tf.log(1 - prediction)))
    train = tf.train.GradientDescentOptimizer(0.1).minimize(loss)

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for i in range(4000):
            sess.run(train, feed_dict={data_X: X.T, data_y: y.T})
        print(sess.run(prediction, feed_dict={data_X: X.T, data_y: y.T}))

# output:
# [[0.00200064]
#  [0.9985947 ]
#  [0.9985983 ]
#  [0.00144795]]
# --------------
# [[0.01765717]
#  [0.98598236]
#  [0.98598194]
#  [0.0207849 ]]
# --------------
# [[0.00104381]
#  [0.9991435 ]
#  [0.49951136]
#  [0.5003463 ]]

Здесь все очень просто, просто используйтеadd\_layerДобавляйте слой за слоем и используйте его напрямуюTensorFlowПодскажите алгоритм оптимизации. Результаты тоже очень странные, иногда хорошие, а иногда и плохие результаты тренировок. . . Возможно, у этого примера высокие требования к значениям инициализации... Конечно, это не исключает, что в моем процессе есть ошибки ?

Этоэкспериментальный код, заинтересованные добро пожаловать Звезда ^_^

4 Резюме

Честно говоря, на подготовку этой статьи ушло много времени, и есть еще некоторые области, где понимание недостаточно глубокое. Ведь это начало нейросети. Я напишу больше статей, чтобы узнать больше в будущее, и я добавлю практические коды, когда у меня будет время. Затем я обнаружил, что математика становится все более и более важной, я обнаружил, что знаю слишком мало, и мне нужно многому научиться. Наконец, вскоре после того, как я столкнулся с машинным обучением, в этой статье неизбежно будут ошибки, и каждый может критиковать и исправлять.

5 ссылок

Примечание: я также спорадически ссылаюсь на другие блоги, а не на основные, поэтому я не нашел ссылку, чтобы опубликовать ее. Спасибо за то, что они поделились, и ответы на некоторые вопросы на Zhihu меня очень вдохновляют, поэтому я не не давайте ссылки по одной.. Некоторые вкрапленные знания даны в двух предыдущих статьях для справки и не будут повторяться здесь.