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]