Создание простого и работоспособного генетического алгоритма для нейросети с Python и NumPy

МЕНЮ


Главная страница
Поиск
Регистрация на сайте
Помощь проекту
Архив новостей

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


Генетический алгоритм нужен, когда ты знаешь параметры своей нейросети, но не знаешь, что должно получиться на выходе, например, этот алгоритм можно использовать для игры в Google динозаврика или Flappy Bird, потому что там ты не знаешь, что должно быть на выходе, но у тебя есть возможность сортировать наиболее жизнеспособные варианты, например по времени, это называется фитнес функций.

У меня никогда не получалось найти такой алгоритм и чтобы он работал, и был простым, и его можно было использовать, поэтому я приступил к созданию своего легкого, простого, прекрасно работающего Genetic Algorithm.

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

Вначале нам потребуется импортировать модули:

import numpy as np import random

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

 x = np.array([[1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0], [0, 1, 1], [1, 1, 1]]) y = np.array([[0],[1],[1], [0], [0], [0], [0], [1], [1]])  #x = np.array([[0, 1, 1], [0, 0, 1], [1, 0, 1], [0, 1, 0], [1, 0, 0], [1, 1, 0], [0, 0, 0], [1, 1, 0], [1, 1, 1]]) #y = np.array([[1],[0], [0], [1], [0], [1], [0], [1], [1]])  #x = np.array([[1, 1, 0], [0, 0, 1], [1, 0, 1], [0, 1, 0], [1, 0, 0], [0, 0, 0], [1, 1, 0], [0, 1, 1], [1, 1, 1]]) #y = np.array([[1],[0],[1], [0], [1], [0], [1], [0], [1]])

Добавляем списки и функции активации. Смысл списков станет понятен позже. Первая функция активации это сигмоида, а вторая - пороговая

listNet = [] NewNet = [] goodNET = [] GoodNet0 = [] GoodNet1 = [] GoodNet2 = [] GoodNet3 = [] GoodNet4 = [] GoodNet5 = [] GoodNet6 = [] good = 0 epoch = 0  good = 0 epoch = 0  def sigmoid(x):     return 1/(1 + np.exp(-x))  def finfunc(x):     if x[0] >= 0.5:         x[0] = 1         if x[1] >= 0.5:             x[1] = 1             return x[1]         else:             x[1] = 0             return x[1]      else:         x[0] = 0         if x[1] >= 0.5:             x[1] = 1             return x[1]         else:             x[1] = 0             return x[1]

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

class Network():     def __init__(self):         self.H1 = np.random.randn(3, 6)         self.O1 = np.random.randn(6, 1)      def predict(self, x, y):         t1 = x @ self.H1         t1 = sigmoid(t1)         t2 = t1 @ self.O1         t2 = sigmoid(t2)         t2 = finfunc(t2)         if t2 == y[0]:             global good             good += 1      def Fpredict(self, x, y):         t1 = x @ self.H1         t1 = sigmoid(t1)         t2 = t1 @ self.O1         t2 = sigmoid(t2)         t2 = finfunc(t2)         if t2 == y[0]:             global good             good += 1         return t2, good class Network1():     def __init__(self, H1, O1):         self.H1 = H1         self.O1 = O1       def predict(self, x, y):         t1 = x @ self.H1         t1 = sigmoid(t1)         t2 = t1 @ self.O1         t2 = sigmoid(t2)         t2 = finfunc(t2)         if t2 == y[0]:             global good             good += 1     def Fpredict(self, x, y):         t1 = x @ self.H1         t1 = sigmoid(t1)         t2 = t1 @ self.O1         t2 = sigmoid(t2)         t2 = finfunc(t2)         if t2 == y[0]:             global good             good += 1         return t2, good  

Выводим первые ответы и переменную good, которая здесь и есть та самая фитнес функция, потом - обнуляем для следующей нейросети, принт 'wait0' (можете сюда написать все, что захочется)нужно, чтобы не запутаться, где начинаются ответы разных нейросетей.

s = Network() print(s.Fpredict(x[0], y[0])) print(s.Fpredict(x[1], y[1])) print(s.Fpredict(x[2], y[2])) print(s.Fpredict(x[3], y[3])) print("wait0") good = 0

Проходит первый цикл, здесь и во всех последующих циклах мы даем, только шесть вопросов, чтобы проверить насколько хорошо она справится с задачей, которую она не встречала, то есть мы проверяем её на зубрежку, а это иногда встречается. А теперь давайте поподробнее: от того насколько ответов она ответила правильно, мы определяем её в один из классов, если большое кол-во правильно, то мы должны поддержать такую нейросеть и увеличить её количество, чтобы при последующей мутации более умных стало больше, чтобы это понять, Вы можете представить, что на 100 человек есть один гений, но его на всех не хватит, и значит его гений угаснет в следующих поколениях, это значит, либо нейросеть будет учиться очень медленно, или вообще не будет, чтобы такого не было мы в цикле увеличиваем количество нейросетей с большим количеством правильных ответов. В конце мы опустошаем основной список listNet присваиваем ему новые значения списков GoodNet в порядке от лучших к худшим, делаем срез на 100 лучших особей, для последующей мутации.

for s in range (1000):     s = Network()     good = 0     s.predict(x[0], y[0])     s.predict(x[1], y[1])     s.predict(x[2], y[2])     s.predict(x[3], y[3])     s.predict(x[4], y[4])     s.predict(x[5], y[5])     if good == 6:         GoodNet6.append(s)         for r in range(15):             GoodNet4.append(s)     elif good == 5:         GoodNet5.append(s)         for r in range(10):             GoodNet4.append(s)     elif good == 4:         GoodNet4.append(s)         for r in range(5):             GoodNet4.append(s)     elif good == 3:         GoodNet3.append(s)     elif good == 2:         GoodNet2.append(s)     elif good == 1:         GoodNet1.append(s)     elif good == 0:         GoodNet0.append(s)     good = 0 listNet = [] listNet.extend(GoodNet6) listNet.extend(GoodNet5) listNet.extend(GoodNet4) listNet.extend(GoodNet3) listNet.extend(GoodNet2) listNet.extend(GoodNet1) GoodNet1 = [] GoodNet2 = [] GoodNet3 = [] GoodNet4 = [] GoodNet5 = [] GoodNet6 = [] goodNET = listNet[:100] listNet = goodNET goodNET = []

Само скрещивание и мутация: мы берём одну часть первого родителя, вторую от второго, мутируем и у нас получился ребёнок в списке NewNet, так 1000 раз.

for g in range(1000):     parent1 = random.choice(listNet)     parent2 = random.choice(listNet)     ch1H = np.vstack((parent1.H1[:1], parent2.H1[1:])) * random.uniform(-0.2, 0.2)     ch1O = parent1.O1 * random.uniform(-0.2, 0.2)     g = Network1(ch1H, ch1O)     NewNet.append(g) listNet = NewNet NewNet = []

Начиная с предыдущей части кода, мы используем Network1(), так как мы сейчас скрещиваем и мутируем, но не создаем рандомно. Так нужно повторить 1000 раз, мы показываем ответы на первый эпохе и 1000-й - финальный вариант. Здесь код повторяется, поэтому я не буду его описывать, там всё очень понятно.

for i in range(1000):     good = 0     epoch += 1     for s in listNet:       good = 0       s.predict(x[0], y[0])       s.predict(x[1], y[1])       s.predict(x[2], y[2])       s.predict(x[3], y[3])       s.predict(x[4], y[4])       s.predict(x[5], y[5])       if good == 6:           GoodNet6.append(s)           for r in range(15):               GoodNet4.append(s)       elif good == 5:           GoodNet5.append(s)           for r in range(10):               GoodNet4.append(s)       elif good == 4:           GoodNet4.append(s)           for r in range(5):               GoodNet4.append(s)       elif good == 3:           GoodNet3.append(s)       elif good == 2:           GoodNet2.append(s)       elif good == 1:           GoodNet1.append(s)       elif good == 0:           GoodNet0.append(s)       good = 0     listNet = []     listNet.extend(GoodNet6)     listNet.extend(GoodNet5)     listNet.extend(GoodNet4)     listNet.extend(GoodNet3)     listNet.extend(GoodNet2)     listNet.extend(GoodNet1)     GoodNet1 = []     GoodNet2 = []     GoodNet3 = []     GoodNet4 = []     GoodNet5 = []     GoodNet6 = []     goodNET = listNet[:100]     listNet = goodNET     goodNET = []     if epoch == 1000:          print(listNet[0].Fpredict(x[0], y[0]))         print(listNet[0].Fpredict(x[1], y[1]))         print(listNet[0].Fpredict(x[2], y[2]))         print(listNet[0].Fpredict(x[3], y[3]))         print(listNet[0].Fpredict(x[4], y[4]))         print(listNet[0].Fpredict(x[5], y[5]))         print(listNet[0].Fpredict(x[6], y[6]))         print(listNet[0].Fpredict(x[7], y[7]))         print(listNet[0].Fpredict(x[8], y[8]))          good = 0         print('wait')     elif epoch == 1:          good = 0         print(listNet[0].Fpredict(x[0], y[0]))         print(listNet[0].Fpredict(x[1], y[1]))         print(listNet[0].Fpredict(x[2], y[2]))         print(listNet[0].Fpredict(x[3], y[3]))         print('wait1')     for g in range(1000):         parent1 = random.choice(listNet)          parent2 = random.choice(listNet)         ch1H = np.vstack((parent1.H1[:1], parent2.H1[1:])) * random.uniform(-2, 2)         ch1O = parent1.O1 * random.uniform(2, 2)         g = Network1(ch1H, ch1O)         NewNet.append(g)     listNet = NewNet

Это всё, закономерность, которую должна найти нейросеть, это от какого числа(первого, второго, третьего) зависит окончательный вариант и на остальные не обращать внимание. Вы можете делать, например, логические операции(XOR, NOT, AND…), только в этом случае в классе network изменить входные данные на два, я также придерживался правила нейроны в скрытом слое равны входным данным умноженным на два, это работало, но вы можете попробовать свои варианты, также очень важно предоставить нейросети одинаковое количество одних ответов и других ответов, чтобы количество правильных ответе, например "a", было бы равно "b", а иначе нейросеть будет отвечать на все ответы одинаково, то есть, если больше a, то она на все ответит a и ничего не выйдет, также давать ей в обучающей выборке совершенно разные варианты, чтобы она поняла закономерность, например если вы делаете блок XOR, то надо обязательно добавить вариант c двумя единичками, но в случае логических операций, там придется давать все варианты, ибо их слишком мало и она ничего не поймет.

Многие могут спросить, а как с помощью этого сделать нейросеть для Google динозаврика и тому подобное. Ответ: используйте три списка, первый: listNet, второй: NewNet, третий: фитнесс функция.

a = []#listNet b = []#NewNet с = []# фитнесс функция d = 0# переменная e = 0# счетчик f = 100 #создавая нейросеть мы добавляем в listNet нейросеть, а в c - время a.append(нейросеть) c.append(время) '''далаете так 100 нейросетй(поэтому в f - 100), в каждой проверяете сколько  она продержалась добавляете время в c, потом выбор 10 самых лучших: ''' for r in range(10):   for i in c:     e += 1     if d < i:       d = i     if e == f:       n = c.index(d)       b.append(a[n])       c.remove(d)       e = 0       f -= 1 a = b b = [] c = [] '''Срез списка не нужен, дальше скрещиваете, мутируете, повторяете и готово!!!'''      

Источник: habr.com

Комментарии: