К книге
Рассказы о математикеСтраница 7
88%
Страница 7
7

220 умноженное на √2, дает как раз 310В. Разумеется, обычный тестер откалиброван в “бытовых” единицах, в режиме измерения переменного тока он покажет 220В. А если выпрямить напряжение, например диодным мостиком, то тестер покажет как раз 310В постоянного тока.

И еще немного про переменный ток. Откуда берется напряжение в 380 вольт? Ток от трансформатора подается по 3м фазам: это 3 линии, напряжение в которых сдвинуто на разный угол друг относительно друга.

Картинка из Википедии:

Нулевой провод - общий. В квартиры подается напряжение с одной из фаз, значением в стандартные 220 вольт. Это напряжение называется фазным. Если же используется 3х-фазная сеть целиком, то напряжение между двумя фазами, например в точках a и с на рисунке, составляет как раз 380 вольт. Это напряжение называется линейным.

Математически, оба напряжения связаны простой формулой:

Действительно, 220*√3 = 380.

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

19. Нейронные сети

Нейроны - биологическая основа всей разумной жизни на Земле, и казалось, бы при чем здесь математика? При том, что как выяснилось еще в 50е годы, модель нейрона математически очень проста:

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

Можно написать, что:

sum = w11*x1 + w12*x2 + … + w1n*xn

out = f(sum)

В качестве функции активации f часто используется “сигмоида” (хотя бывают и другие варианты):

Как можно видеть, все весьма просто - математически нейрон лишь чуть сложнее чем обыкновенный сумматор. Выходы нейрона благодаря функции активации лежат в диапазоне 0..1.

Что может делать один нейрон? Практически ничего полезного, но если объединить их в сеть, возможности возрастают кардинально. Например, сеть из 6 нейронов уже способна воспроизвести любую логическую функцию, что мы сейчас и продемонстрируем.

Рассмотрим структуру сети, называемую “многослойный перцептрон” (multilayer perceptron):

Как можно видеть, она состоит из входного слоя нейронов (input layer), скрытого слоя (hidden layer) и выходного слоя (output layer). Каждый нейрон скрытого слоя соединен со всеми входными нейронами, плюс введено дополнительное “смещение” (bias), которое можно представить как нейрон, вход которого всегда = 1 и вес равен константе (например 0.5). Дальше структура повторяется. Количество входных, выходных и скрытых нейронов может быть различно.

Процесс перехода сигнала по сети называется прямым распространением (forward propagation). Допустим, на входе имеются значения: x1 = 0, x2 = 1. Тогда прямое распространение выглядит так:

Шаг-1: передача сигнала от входа во внутренний (hidden) слой

h1 = f(x1*w1 + x2*w2 + 0.5*1)

h2 = f(x1*w3 + x2*w4 + 0.5*1)

Шаг-2: передача сигнала от внутреннего в наружный (output) слой

o1 = f(h1*w5 + h2*w6 + 0.5*1)

o2 = f(h1*w7 + h2*w8 + 0.5*1)

Собственно и все, мы подали на вход сети сигнал “х” и получили на выходе сигнал “о”. Однако внимательный читатель может задать вопрос - а как задаются коэффициенты w1, w2,..,w8? Это собственно, самое интересное и сложное, процесс настройки параметров сети и называется обучением. Задавая различные наборы входных и выходных данных, мы можем обучить сеть воспроизводить требуемые нам функции. Для данного типа сетей используется так называемый алгоритм обратного распространения (back propagation). Его суть в модификации коэффициентов w от конца сети к началу, примерно это можно выразить так:

Шаг-0: Значение коэффициентов w1..w8 заполняются некоторыми случайными величинами.

Шаг-1: Берем набор входных данных, получаем значения выхода (output) методом уже рассмотренного “прямого распространения”.

Шаг-2: Сравниваем значения output с “целевыми” значениями (target). Для удобства сравнения часто используется функция квадрата разности: Err = (output - target)2

Шаг-3: Модифицируем “выходные” коэффициенты w5..w8 так, чтобы значение Err уменьшалось. Для этого используется метод так называемого “градиентного спуска”: функцию output всей нейронной сети в целом легко можно выразить как последовательность сумм и произведений (x1*w1 + x2*w2 …), это значит, что продифференцировав ее по нужному весовому параметру, легко найти производную, например dOutput/dw5. Как известно, производная показывает направление роста функции, что нам и нужно - мы можем изменить параметр w5 на величину, обратную производной.

Шаг-4: Выполняем аналогичный процесс для “входных” коэффициентов w1..w4, формулы тут примерно аналогичны предыдущему пункту.

Шаги 1..4 повторяются до тех пор, пока ошибка не станет меньше некоторой величины. Этот процесс весьма медленный, и может требоваться несколько тысяч итераций, однако выполняется он лишь однократно. Далее коэффициенты можно сохранить, и сеть уже обучена воспроизводить указанный при обучении набор данных. Эти весовые коэффициенты фактически и являются аналогом нейронных связей, появляющихся после обучения в “биологической” сети.

Более подробно формулы с примерами вычислений можно найти здесь. А мы рассмотрим программу на языке Python, реализующую данный метод.

import math, random

class MLP(object):

def __init__(self, n_in, n_hidden, n_out):

self.n_in = n_in

self.n_hidden = n_hidden

self.n_out = n_out

# Входные данные: массив [n_in + 1] инициализирован значениями 1.0

self.inputLayer = [1.0] * (n_in + 1)

# Веса входного-скрытого слоя: матрица [n_in+1]x[n_hidden] заполнена 0..1

self.wIH = [[random.random() for x in range(n_in+1)] for y in range(n_hidden)]

# Скрытые нейроны: [n_hidden + 1]

self.hiddenLayer = [1.0] * (n_hidden + 1)

# Веса нейронов скрытого-выходного слоя: [n_hidden+1]x[n_out], 0..1

self.wHO = [[random.random() for x in range(n_hidden+1)] for y in range(n_out)]

# Выходной слой: массив

self.outputLayer = [1.0] * n_out

def printValues(self):

print "Network: input/hidden/output: {}/{}/{}".format(self.n_in, self.n_hidden, self.n_out)

print "Вход", self.inputLayer

print "wIH", self.wIH

print "Скрытый слой", self.hiddenLayer

print "wHO", self.wHO

print "Выход", self.outputLayer

def printOutput(self):

print "Вход", self.inputLayer

print "Выход", self.outputLayer

def sigmoid(self, x):

# Функция активации

return 1.0/(1.0 + math.exp(-x))

def derivative(self, x):

# Производная функции активации

return x*(1 - x)

def forwardPass(self, x):

# Прямое распространение

# Input

for p in range(len(x)):

self.inputLayer[p] = x[p]

# Input-Hidden

for h in range(self.n_hidden):

sum = 0.0

for i in range(len(self.inputLayer)):

sum += self.inputLayer[i]*self.wIH[h][i]

self.hiddenLayer[h] = self.sigmoid(sum)

# Hidden-Output

for o in range(self.n_out):

sum = 0.0

for h in range(len(self.hiddenLayer)):

sum += self.hiddenLayer[h]*self.wHO[o][h]

self.outputLayer[o] = self.sigmoid(sum)

def backPass(self, input, target):

# Обратное распространение

# Формулы основаны на статье

# https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/

learn_rate = 0.5

# Output->hidden layer adjust weights

for o in range(self.n_out):

for h in range(self.n_hidden):

deout = -(target[o] - self.outputLayer[o])

der = self.derivative(self.outputLayer[o])

hid = self.hiddenLayer[h]

der = deout*der*hid

prev = self.wHO[o][h]

self.wHO[o][h] -= learn_rate*der

# Hidden->input layer adjust weights

for h in range(self.n_hidden):

for i in range(self.n_in):

derSum = 0.0

for o in range(self.n_out):

deout = -(target[o] - self.outputLayer[o])

derOut = self.derivative(self.outputLayer[o])

der = deout*derOut*self.wHO[o][h]

derSum += der

derH = self.derivative(self.hiddenLayer[h])

derI = self.inputLayer[i]

der = derSum*derH*derI

prev = self.wIH[h][i]

self.wIH[h][i] -= learn_rate*der

def calcError(self, x, target):

# Вычисление ошибки: 0.5*Sum(dV^2)

err = 0.0

for p in range(self.n_out):

err += 0.5*((self.outputLayer[p] - target[p])**2)

return err

Как можно видеть, создан класс MLP, принимающий на входе 3 параметра: число входных нейронов, число скрытых нейронов, число выходных нейронов. Рассмотрим пример использования этой сети для логической функции XOR. Как известно, она имеет следующую таблицу истинности:

Таким образом, в нашей сети будет 2 входных параметра (их еще называют “характеристиками”) и 1 выходной. Количество скрытых нейронов пусть будет 2.

Код на языке Python выглядит следующим образом.

mlp = MLP(n_in=2, n_hidden=2, n_out=1)

print("Тренировка сети")

inputs = [ [0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0] ]

targets = [ [0.0], [1.0], [1.0], [0.0] ]

N = 500000

for p in range(N):

rand_index = random.randrange(0, len(inputs))

mlp.forwardPass(inputs[rand_index])

mlp.backPass(inputs[rand_index], targets[rand_index])

err = mlp.calcError(inputs[rand_index], targets[rand_index])

if p % 10000 == 0:

print("Step", p, "error", err)

print("")

print("Done")

mlp.printValues()

print("")

print("Результаты")

mlp.forwardPass([ 0.0, 0.0 ])

mlp.printOutput()

mlp.forwardPass([ 0.0, 1.0 ])

mlp.printOutput()

mlp.forwardPass([ 1.0, 0.0 ])

mlp.printOutput()

mlp.forwardPass([ 1.0, 1.0 ])

mlp.printOutput()

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

В результате получаем следующие наборы параметров W:

Wih = [[10.276084, 9.57721341, 0.11424834], [0.86694222, 0.86379, 0.728575]]

Who = [53.976871, -61.523507, 0.13521682]

Напомним, что весовые коэффициенты фактически соответствуют нейронным связям, возникающим в “настоящих” биологических сетях. Тестирование показывает, что обученная сеть действительно воспроизводит функцию XOR практически достоверно, лишь с небольшой погрешностью:

Input [0.0, 0.0], output [0.0000266]

Input [0.0, 1.0], output [0.95193190]

Input [1.0, 0.0], output [0.95074983]

Input [1.0, 1.0], output [0.07137252]

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

Network: input/hidden/output: 2/3/1

Train phase:

Step 0 error 0.418338057819

Step 10000 error 0.00320960246906

Step 20000 error 0.00115714673178

Step 30000 error 0.00176164027913

Step 40000 error 4.34585435915e-05

wIH [[6.243515, -4.35533, 0.1199493], [5.040033, 5.132244, 0.35661004], [-4.4208235, 6.207919, 0.2522050]]

wHO [-11.2227881, 14.0531446, -11.2731065, 0.9389702]

Test phase:

Input [0.0, 0.0], output [0.0436959]

Input [0.0, 1.0], output [0.9712140]

Input [1.0, 0.0], output [0.9721711]

Input [1.0, 1.0], output [0.0075550]

Предыдущая главаГлава 7 из 8Следующая глава