# DOCS ON ENG: [Here](pypi.org/project/easymyai/)


# КАК СКАЧАТЬ
```
pip install easymyai 
```

# ДОКУМЕНТАЦИЯ
SimpleMyAI это небольшая библиотека для создания собственного простенького ИИ. Я её написал полностью с нуля, используя только numpy, а из интернета я брал ТОЛЬКО основные принципы работы. Также я иставил кучу комментариев к коду, чтобы вы могли сами разобраться как здесь всё работает и покапаться в коде (может даже что-то новое узнаете)

# Как использовать мою библиотеку: 👉
# Супер кратко:

```python
from My_AI import AI_ensemble, AI

# Создаём ИИ
ai = AI(architecture=[10, 50, 50, 1],
        add_bias=True,
        name="First_AI")
""" Или можно создать ансамбль
ai = AI_ensemble(amount_ais=10, architecture=[10, 50, 50, 1],
                      add_bias_neuron=True,
                      name="First_AI")
"""

# Устанавливаем коэффициенты
ai.alpha = 1e-3  # Скорость обучения
ai.batch_size = 10  # Размер батча
# Для оптимизатора Adam
ai.impulse1 = 0.9  # Обычно от 0.8 до 0.999
ai.impulse2 = 0.999  # Немного отличется от beta1
# Регуляризация
ai.l1 = 0.0  # L1 регуляризация
ai.l2 = 0.0  # L2 регуляризация

ai.what_act_func = ai.kit_act_func.tanh
ai.end_act_func = ai.kit_act_func.tanh
ai.save_dir = "Saves AIs"  # В какую папку сохраняем ИИшки

# Обучаем (Например распозновать картинки)
for image in dataset:
    data = image.tolist()
    answer = [image.what_num]
    ai.learning(data, answer, squared_error=True)

""" Есть также и Q-бучение (функции обновления таблицы см. ниже)
actions = ("left", "right", "up", "down")
ai.make_all_for_q_learning(actions, ai.kit_upd_q_table.standart,
                           gamma=0.5, epsilon=0.01, q_alpha=0.1)

state, reward = game.get_state_and_reward()
ai.q_learning(state, reward,
              learning_method=2.5, squared_error=False)
"""
```




# Подробности:

#### Можете скопировать пакет "easymyai" к себе в проект (всё остальное просто примеры использования), и импоритровать от туда классы AI или AI_ensemble

### Как инициализировать ИИшку:
```python
from My_AI import AI_ensemble, AI

ai = AI()
# Или
ensemble = AI_ensemble(5) # 5 —— количество ИИшек в ансамбле
```
> Ансамбль — это несколько ИИ в одной коробке, которые вместе принимают решение (ансамбль подходит для Q-обучения (обучение с подкреплением; когда нету "правильного" и "неправильного" ответа, а только вознаграждение за какое-то выбранное действие))

> P.s. В качестве примера посмотрите на ИИ для змейки (в файле "AI for snake.py")


### • Чтобы использовать ИИшку надо создать архитектуру одним из способов:
```python
ai = AI(architecture=[3, 4, 4, 4, 3],
        add_bias_neuron=True,
        name="First_AI")

""" Если используете ансамбль
ensemble = AI_ensemble(5, architecture=[3, 4, 4, 4, 3],
                       add_bias_neuron=True,
                       name="First_AI")
"""
```
или

```python
ai.create_weights([3, 4, 4, 4, 3], add_bias=True)
ai.name = "First_AI"
# Имя не обязательно указывать имя, но тогда при сохранении
# будет использовано случайное число вместо имени
```
Таким образом будет создана следущая архитектура:
<div id="header" align="left">
  <img src="https://i.ibb.co/nbbTLZS/Usage-example.png" width="600"/>
</div>


####  
### • Гаперпараметры:

```python
"""Прописывать или изменять все гаперпараметры необязательно"""

ai.alpha = 1e-2  # Скорость обучения

ai.disabled_neurons = 0.0  # Какую долю нейронов отключаем
# (Это надо чтобы не возникало переобучение)

ai.batch_size = 10  # Сколько ответов усредняем, чтобы на них учиться
# (Ускоряет обучение и (иногда, далеко не всегда) улучшает качество обучения)

# Функция активации нейронов (крайне рекомендую оставить tanh если
# есть такая возможность, т.к. с ним ИИ работает в разы быстрее)
ai.what_act_func = ai.kit_act_func.tanh

# Функция активации для последнего слоя (аналогично, рекомендую оставить tanh)
# P.s. end_act_func может и отстутсвовать (т.е. можно установить None)
ai.end_act_func = ai.kit_act_func.tanh

# Коэффициенты имапульса для оптимизатора Adam
ai.impulse1 = 0.9  
ai.impulse2 = 0.999
# Если не знаете что такое оптимизатор погуглите, очень интересно))))

# Коэффициенты для регуляризации весов 
# (Регуляризация — удержание весов около 0 (или же [-1; +1]) )
ai.l1 = 0.001  # НА сколько уменьшаем веса (устремляет веса прямо к 0)
ai.l2 = 0.01   # ВО сколько уменьшаем веса (удерживает веса около 0)
```


####  
### • Обучение:

```python
# На вход и на выход нейросети надо просто подавать список чисел
data = [0, 1, 2]   # Входные данные
answer = [0, 1, 0] # Выходные данные

ai.learning(data, answer,
            squared_error=True)
"""
Квадратичная ошибка позволяет быстрее обучаться на больших косяках
и закрывать глаза на мелкие недочёты (но иногда лучше её отключать)
"""
```

### Q-бучение:
> Q-обучение (оно же обучение с подкреплением) — это когда нету "правильного" и "неправильного" ответа, а только вознаграждение за какое-то выбранное действие, т.е. на сколько оно хорошее 

> (0 = нейтральный выбор, <0 = плохой выбор, >0 = хороший выбор)

```python
# ИИшка может только выбрать какое-то конкретное действие из возмажных
all_possible_actions = ["left", "right", "up", "down"]

gamma = 0.4     # Коэффициент "доверия опыту" (для "сглаживания" Q-таблицы)
epsilon = 0.15  # Доля на случайных действий (чтобы ИИшка изучала окружающую среду)
q_alpha = 0.1   # Скорость обновления Q-таблицы (на самом деле оно почти ни на что не влияет) 

ai.make_all_for_q_learning(all_possible_actions,
                           ai.kit_upd_q_table.standart,
                           gamma=gamma, epsilon=epsilon, q_alpha=q_alpha)
# Функции обновления таблицы довольно сильно влияют на обучение

# Как и с обычным обучением, на вход подаём просто список чисел (состояние)
ai_state = [0, 1]  # Например координаты нейронки

ai.q_learning(ai_state, reward_for_state, learning_method=1,
              recce_mode=False, squared_error=True)

# Какое решение приняла ИИшка при определённых данных
predict = ai.q_predict(ai_state)
```
> recce_mode: Режим "исследования окружающей среды" (постоянно выбирать случайное действие)

> Методы обучения (значение learning_method определяет) :
> - 1 : В качестве "правильного" ответа выбирается то, которое максимально вознаграждается, и
на место действия (которое приводит к лучшему ответу) ставиться максимальное значение функции
активации, а на остальные места минимум функции активации
P.s. Это неочень хорошо, т.к. игнорируются другие варианты, которые приносят либо столько же,
либо немного меньше вознаграждения (а вибирается только один "правильный"). НО ОН ХОРОШО ПОДХОДИТ,
КОГДА У ВАС В ЗАДАЧЕ ИМЕЕТСЯ ИСКЛЮЧИТЕЛЬНО 1 ПРАВИЛЬНЫЙ ОТВЕТ, А "БОЛЕЕ" И "МЕНЕЕ" ПРАВИЛЬНЫХ БЫТЬ НЕ МОЖЕТ
> - 2 : Делаем ответы которые больше вознаграждаются, более "правильным". Дробная часть числа означает, в какую степень будем возводить "стремление у лучшим результатам" (НАПРИМЕР: 2.3 означает, что мы используем метод обучения 2 и возводим в степень 3 "стремление у лучшим результатам", а 2.345 означает, что степень будет равна 3.45 )

####  
### • Ну и конечно же сохранения и загрузки нейросети
```python
ai.save()  # Сохраниться под текущим именем
ai.save("First_AI")  # Сохраниться под названием First_AI ()
```
> Ещё можно выбрать свою папку для сохранений ИИшек (зачем? сам хз)
```python
# Всё будет сохраняться в папку SAVES рядышком с пакетом My_AI
ai.save_dir = "SAVES"
```

####  
### • Также ради прикола сделал и генетическое обучение
> Это когда перемешиваются веса у ИИшек

```python
# Тут мы у ai_0 перемешиваем веса с ai_1, а ai_1 не трогаем
better_ai_0.genetic_crossing_with(better_ai_1)
```
### • В дополнение к генетическому алгоритму создал возможность создание мутаций
> Какую-то долю весов заменяем на случайные числа от -1 до 1 

```python
ai.make_mutations(0.05)  # 5% весов оказываются случайными числами
```


####  
### Ещё можно подать данные на выходные нейроны и получить из входных (Зачае? я сам не знаю, но это может быть полусным)
```python
new_data = ai.predict(data, reverse=True)
new_data = ai.predict(new_data)

# new_data == data
```

####  
#### • Кстати, очень советую переводить входные числа в промежуток от -1 до 1 (или от 0 до 1) 
> Просто для ИИшки проще работать с числами от -1 до 1 (или от 0 до 1) чем с непонятными огромными значениями

```python
# Проще использовать normalize, но можно и tanh (или sigmoid)
ai.kit_act_funcs.normalize(data, 0, 1)
```


#  
#  
#  
###### _Удачи в ловле жучков_ (づ｡◕‿‿◕｡)づ
