import random
import numpy as np

class Perceptron3:
    Steps = 0  # число шагов обучения
    DeltaW = 0.0  # величина веса обучения
    Teta = 0  # величина порога
    NS = 0  # количество связей между рецепторным слоем и ассоциативным слоем
    NR = 0  # размер рецепторного слоя
    NA = 0  # размер ассоциативного слоя
    ImageSize = 0  # интервал изменения размеров образов
    ImageMove = 0  # интервал перемещения образа по рецепторному слою

    def __init__(
            self,
            steps=70000,
            deltaW=100,
            teta=2,
            ns=29,
            nr=21,  # Увеличено для лучшей детализации
            na=21,
            imageSize=15,
            imageMove=15):
        self.NS = ns
        self.Teta = teta
        self.DeltaW = deltaW / 100
        self.Steps = steps
        self.NR = nr
        self.NA = na
        self.ImageMove = nr if imageMove > nr else imageMove
        self.ImageSize = nr if imageSize > nr else imageMove

        self.R = np.zeros((self.NR, self.NR))  # слой рецепторов
        self.A = np.zeros((self.NA, self.NA))  # ассоциативный слой
        self.S = [[[] for _ in range(self.NR)] for _ in range(self.NR)]  # Coordinates[NR, NR, NS] связи
        self.W = np.zeros((self.NA, self.NA, 3))  # веса связей

    class Coordinates:
        R = 0  # строка
        C = 0  # столбец

    fTest = False  # флаг проверки работоспособности
    # Обновленный словарь для новых фигур
    names = {
        '000': 'круг',
        '001': 'квадрат',
        '010': 'ромб',
        '011': 'сердце',
        '100': 'треугольник'
    }

    def InitLayers(self):  # инициализация слоёв
        # генерация связей
        for r in range(self.NR):
            for c in range(self.NR):
                for k in range(self.NS):  # генерация связей
                    coord = self.Coordinates()
                    coord.R = random.randint(0, self.NA - 1)
                    coord.C = random.randint(0, self.NA - 1)
                    self.S[r][c].append(coord)
        # очистка весов
        self.W.fill(0)

    def CreateVLine(self, r1, r2, c):
        min_r = min(r1, r2)
        max_r = max(r1, r2)
        for i in range(min_r, max_r + 1):
            if 0 <= c < self.NR and 0 <= i < self.NR:
                self.R[i][c] = 1

    def CreateHLine(self, c1, c2, r):
        min_c = min(c1, c2)
        max_c = max(c1, c2)
        for i in range(min_c, max_c + 1):
            if 0 <= r < self.NR and 0 <= i < self.NR:
                self.R[r][i] = 1

    def NameDic(self, dic):  # имя образа: круг, квадрат, ромб, сердце, треугольник
        return self.names[dic] if dic in self.names else 'не знаю'

    def CreateImage(self, step):  # создание образа по шагу
        # Чистка рецепторов
        self.R.fill(0)
        sd = step % 5  # выбор образа (5 фигур)
        rr = random.randint(4, int(self.ImageSize / 2))  # выбор размера образа
        res = ''
        # выбор места расположения образа
        rc = rr + random.randint(0, self.ImageMove - 2 * rr - 1)  # строка центра
        cc = rr + random.randint(0, self.ImageMove - 2 * rr - 1)  # столбец центра

        if sd == 0:  # круг
            res = "000"
            for r in range(self.NR):
                for c in range(self.NR):
                    # Нормализуем координаты от -1 до 1
                    x = (c - cc) / (self.NR / 2)
                    y = (rc - r) / (self.NR / 2)
                    if x**2 + y**2 <= (rr / (self.NR / 2))**2:
                        self.R[r][c] = 1
        elif sd == 1:  # квадрат
            res = "001"
            self.CreateHLine(cc - rr, cc + rr, rc - rr)
            self.CreateHLine(cc - rr, cc + rr, rc + rr)
            self.CreateVLine(rc - rr, rc + rr, cc - rr)
            self.CreateVLine(rc - rr, rc + rr, cc + rr)
        elif sd == 2:  # ромб
            res = "010"
            for i in range(-rr, rr + 1):
                if 0 <= rc + i < self.NR:
                    c_offset = rr - abs(i)
                    if 0 <= cc - c_offset < self.NR:
                        self.R[rc + i][cc - c_offset] = 1
                    if 0 <= cc + c_offset < self.NR:
                        self.R[rc + i][cc + c_offset] = 1
        elif sd == 3:  # сердце
            res = "011"
            # Улучшенная реализация: две окружности и перевернутый треугольник

            # Параметры сердечка
            circle_radius = rr // 2  # Радиус верхних окружностей
            triangle_height = rr  # Высота нижнего треугольника

            # Координаты центров двух верхних окружностей
            center_offset = rr // 2
            center1 = (rc - center_offset, cc - center_offset)
            center2 = (rc - center_offset, cc + center_offset)

            # Рисуем две верхние окружности
            for r in range(self.NR):
                for c in range(self.NR):
                    # Первая окружность
                    dist1 = (r - center1[0])**2 + (c - center1[1])**2
                    # Вторая окружность
                    dist2 = (r - center2[0])**2 + (c - center2[1])**2
                    if dist1 <= circle_radius**2 or dist2 <= circle_radius**2:
                        self.R[r][c] = 1

            # Рисуем перевернутый треугольник
            for i in range(triangle_height):
                current_r = rc + i
                start_c = cc - (triangle_height - i)
                end_c = cc + (triangle_height - i)
                if 0 <= current_r < self.NR:
                    for c in range(start_c, end_c + 1):
                        if 0 <= c < self.NR:
                            self.R[current_r][c] = 1

        elif sd == 4:  # треугольник
            res = "100"
            for i in range(rr):
                if rc - rr + i >= 0 and rc - rr + i < self.NR:
                    if cc - i >= 0 and cc - i < self.NR:
                        self.R[rc - rr + i][cc - i] = 1
                    if cc + i >= 0 and cc + i < self.NR:
                        self.R[rc - rr + i][cc + i] = 1
            self.CreateHLine(cc - rr, cc + rr, rc)

        # вывод образа на экран во время проверки работы
        if self.fTest:
            print("Рисую " + self.NameDic(res))
            for r in range(self.NR):
                for c in range(self.NR):
                    if self.R[r, c] == 0:
                        print(".", end="")
                    else:
                        print("X", end="")
                print()
        return res

    def Mapping(self):
        # отображение образа
        self.A.fill(0)
        for r in range(self.NR):  # отображение в слое A (входы)
            for c in range(self.NR):
                if self.R[r, c] == 1:
                    for coord in self.S[r][c]:
                        rv = coord.R
                        cv = coord.C
                        if 0 <= rv < self.NA and 0 <= cv < self.NA:
                            self.A[rv][cv] += 1
        for r in range(self.NA):  # отображение в слое A (выходы)
            for c in range(self.NA):
                if self.A[r][c] > self.Teta:
                    self.A[r][c] = 1
                else:
                    self.A[r][c] = 0

    def Reaction(self):
        # распознавание образа: 1000..0001
        E = np.zeros((3))  # эффекторный слой
        res = ""  # код распознавания
        for k in range(3):
            E[k] = np.sum(self.A * self.W[:, :, k])
            res += '1' if E[k] > 0 else '0'
        # вывод результата при проверке работоспособности
        if self.fTest:
            print("\nЯ думаю, что это " + self.NameDic(res))
        return res

    def Teach(self, expected, current):
        # обучение нейросети
        for k in range(3):
            if expected[k] != current[k]:
                for r in range(self.NA):
                    for c in range(self.NA):
                        if self.A[r][c] == 1:
                            # обучение виноватых
                            self.W[r][c][k] += self.DeltaW if expected[k] == '1' else -self.DeltaW

    def Run(self):
        print("Обучение перцептрона распознаванию пяти фигур: круг, квадрат, ромб, сердце, треугольник")
        lmax = 40000  # число шагов обучения
        nOk = 0  # число удачных ответов
        self.InitLayers()  # инициализация слоёв
        for step in range(1, lmax + 1):
            expected = self.CreateImage(step)  # новый образ
            self.Mapping()  # отображение
            result = self.Reaction()  # опознавание
            if expected == result:
                nOk += 1
            else:
                self.Teach(expected, result)  # обучение

            # вывод текущей информации на экран
            if step % 1000 == 0 or step <= 20:
                print("\rШаг {step}: Доля удачных ответов {percent:.2f}%".format(
                    step=step, percent=(nOk * 100 / step)), end='')
            # тестирование за последние 20 шагов обучения
            if step == (lmax - 20):
                self.fTest = True
        print("\nОбучение завершено.")

# Создание и запуск перцептрона
perceptron = Perceptron3()
perceptron.Run()

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