Содержание статьи
Как создать нейросеть с помощью chatGPT
Как можно улучшить нейронную сеть
Больше мощностей. Нейронные сети работают с матрицами, так что если нейронов много, вычисления получаются очень ресурсоемкие. Известные нейросети вроде Midjourney или ChatGPT — это сложные и «тяжелые» системы, для их работы нужны сервера с мощным «железом». Так что написать собственный DALL-E на домашнем компьютере не получится. Но есть сервисы для аренды мощностей: ими как раз пользуются инженеры машинного обучения, чтобы создавать, обучать и тестировать модели.
Только ленивый не слышал сегодня о существовании и разработке нейронных сетей и такой сфере, как машинное обучение. Для некоторых создание нейросети кажется чем-то очень запутанным, однако на самом деле они создаются не так уж и сложно. Как же их делают? Давайте попробуем самостоятельно создать нейросеть прямого распространения, которую еще называют многослойным перцептроном. В процессе работы будем использовать лишь циклы, массивы и условные операторы. Что означает этот набор данных? Только то, что нам подойдет любой язык программирования, поддерживающий вышеперечисленные возможности. Если же у языка есть библиотеки для векторных и матричных вычислений (вспоминаем NumPy в Python), то реализация с их помощью займет совсем немного времени. Но мы не ищем легких путей и воспользуемся C#, причем полученный код по своей сути будет почти аналогичным и для прочих языков программирования.
Но полученный вектор представляет собой неактивированное состояние (промежуточное, невыходное) всех нейронов, а для того, чтобы нам получить выходное значение, нужно каждое неактивированное значение подать на вход вышеупомянутой функции активации. Итогом ее применения и станет выходное значение слоя.
Сделай код запуска этой модели в цикле с разными размерами batch_size (32, 64, 128), выведи график точности по каждому запуску. Ответ chatGPT: batch_size_list = [ 32, 64, 128 ] for batch_size in batch_size_list: # Создаем более мощную модель model = Sequential() model.add(Conv2D( 32 , ( 3, 3 ), activation= ‘relu’ , input_shape=( 64, 64, 3 ))) model.add(BatchNormalization()) model.add(MaxPooling2D(( 2, 2 ))) model.add(Conv2D(64, ( 3, 3 ), activation= ‘relu’ )) model.add(BatchNormalization()) model.add(MaxPooling2D(( 2, 2 ))) model.add(Conv2D( 128 , ( 3, 3 ), activation= ‘relu’ )) model.add(BatchNormalization()) model.add(MaxPooling2D(( 2, 2 ))) model.add(Flatten()) model.add(Dropout( 0.5 )) model.add(Dense(num_classes, activation= ‘softmax’ )) model. compile (optimizer=’adam’, loss= ‘categorical_crossentropy’ , metrics=[ ‘accuracy’ ]) # Обучаем модель history = model.fit(X_train, y_train, epochs=10, batch_size=batch_size,
validation_data=(X_test, y_test))
# Рисуем график точности по эпохам обучения plt.plot(history.history[‘accuracy’], label= f ‘batch size : ‘ ) plt.title( ‘Model accuracy with different batch sizes’ ) plt.ylabel( ‘Accuracy’ ) plt.xlabel( ‘Epoch’ ) plt.legend(loc= ‘upper left’ ) plt.show()
Таким образом, мы получили (хоть и не с первого раза) то, что хотели (почти). Стоило добавить, что графики нужно было отрисовывать по точности на проверочной выборке.
Для уменьшения ошибки нейронной сети надо поменять весовые коэффициенты, причем послойно. Каким же образом это осуществить? Ничего сложного в этом нет: надо воспользоваться методом градиентного спуска. То есть нам надо рассчитать градиент по весам и сделать шаг от полученного градиента в отрицательную сторону. Давайте вспомним, что на этапе прямого распространения мы запоминали входные сигналы, а во время обратного распространения ошибки вычисляли дельты, причем послойно. Как раз ими и надо воспользоваться в целях нахождения градиента. Градиент по весам будет равняться не по компонентному перемножению дельт и входного вектора. Дабы обновить весовые коэффициенты, снизив таким образом ошибку нейросети, нужно просто вычесть из матрицы весов итог перемножения входных векторов и дельт, помноженный на скорость обучения. Все вышеперечисленное можно записать в следующем виде:
Например, на вход поступает картинка. Чтобы нейросеть могла понять, что на ней изображено, она должна выделить разные элементы из картинки, распознать их и подумать, что означает сочетание этих элементов. Примерно так работает зрительная кора в головном мозге. Это несколько задач, их не смогут решить одинаковые нейроны. Поэтому нужно несколько слоев, где каждый делает что-то свое. Для распознавания часто используют так называемые сверточные нейросети. Они состоят из комбинации сверточных и субдискретизирующих слоев, каждый из которых решает свою задачу.
Теперь напиши, как дополнить этот код, чтобы запустить обучение модели на 10 эпохах, построй график точности по эпохам. Ответ chatGPT: import matplotlib.pyplot as plt # Обучаем модель history = model.fit(X_train, y_train, epochs=10, validation_data=(X_test, y_test)) # Рисуем график точности по эпохам обучения plt.plot(history.history[ ‘accuracy’ ]) plt.plot(history.history[ ‘val_accuracy’ ]) plt.title( ‘Model accuracy’ ) plt.ylabel( ‘Accuracy’ ) plt.xlabel( ‘Epoch’ ) plt.legend([ ‘Train’ , ‘Test’ ], loc= ‘upper left’ ) plt.show()
Текстовый комментарий chatGPT: «Добавив этот код, мы запускаем обучение модели на 10 эпохах. Мы сохраняем результат обучения в переменной history, чтобы построить график точности модели по эпохам. Мы используем plt.plot() для отображения истории обучения из history.history[‘accuracy’] (точность на обучающем наборе) и history.history[‘val_accuracy’] (точность на тестовом наборе). И далее добавляем легенду и отображаем график».
Лучше обучение. Искусственные нейронные сети обучаются примерно по тому же принципу, что живые существа. Когда человек часто повторяет одни и те же действия, он учится: ездить на велосипеде, рисовать или набирать текст. Это происходит, потому что веса между нейронами в мозгу меняются: нервные клетки наращивают новые связи, по-новому начинают воспринимать сигналы и правильнее их передают. Нейронная сеть тоже изменяет веса при обучении — чем оно объемнее, тем сильнее она «запомнит» какую-то закономерность.
Формула для расчета выхода нейрона
Больше нейронов. В нашей тренировочной нейросети только один нейрон. Но если нейронов будет больше — каждый из них сможет по-своему реагировать на входные данные, соответственно, на следующие нейроны будут приходить данные с разных синапсов. Значит — больше вариативность, «подумать» и передать сигнал дальше может не один нейрон, а несколько. Можно менять и формулу передачи, и связи между нейронами — так получаются разные виды нейронных сетей.
Когда мы узнаем дельту последнего слоя, мы сможем найти дельты и всех предыдущих слоев. Чтобы это сделать, нужно будет лишь перемножить для текущего слоя транспонированную матрицу с дельтой, а потом перемножить результат с вектором производных функции активации предыдущего слоя:
Но нейронные сети — все же не человеческий мозг. Мозг сложнее, объемнее, в нем намного больше нейронов, чем в любой компьютерной нейросети. Поэтому чрезмерное обучение может сделать хуже. Например, переобученная нейросеть может начать распознавать предметы там, где их нет — так люди иногда видят лица в фарах машин и принимают пакеты за котов. А в случае с искусственной нейронной сетью такой эффект еще явнее и заметнее. Если же учить нейросеть на нескольких разнородных данных, скажем, сначала обучить считать числа, а потом — распознавать лица, она просто сломается и начнет работать непредсказуемо. Для таких задач нужны разные нейросети, разные структуры и связи.
Давайте внимательно посмотрим на него. Вышенаписанная формула — это не что иное, как определение умножения матрицы на вектор. И в самом деле, если мы возьмем матрицу W размера n на m и выполним ее умножение на X размерности m, то мы получим другое векторное значение n-размерности, то есть как раз то, что надо.
Говоря проще, ИНС можно назвать неким «черным ящиком», превращающим входные данные в выходные данные. Если же посмотреть на это с точки зрения математики, то речь идет о том, чтобы отобразить пространство входных X-признаков в пространство выходных Y-признаков: X → Y. Таким образом, нам надо найти некую F-функцию, которая сможет выполнить данное преобразование. На первом этапе этой информации достаточно в качестве основы.
Другие методы и формулы. Чтобы нейроны обучались, нужно задать формулу корректировки весов — мы говорили про это выше. Если нейронов много, то формулу нужно как-то распространить на все из них. Для этого используется метод градиентного спуска: рассчитывается градиент по весам, а потом от него делается шаг в меньшую сторону. Звучит сложно, но на самом деле для этого есть специальные формулы и функции.
Так часто происходит в реальных задачах, например, при распознавании предметов. Не у всех из них есть жесткие критерии: скажем, гипертрофированного мультяшного персонажа мы по-прежнему различаем как человека, хотя у него совсем другие пропорции. Нейронную сеть сложно научить похожему — но современные системы могут справиться и с этим.
Один нейрон может превратить в одну точку входной вектор, но по условию мы желаем получить несколько точек, т. к. выходное Y способно иметь произвольную размерность, которая определяется лишь ситуацией (один выход для XOR, десять выходов, чтобы определить принадлежность к одному из десяти классов, и так далее). Каким же образом получить n точек? На деле все просто: для получения n выходных значений, надо задействовать не один нейрон, а n. В результате для каждого элемента выходного Y будет использовано n разных взвешенных сумм от X. В итоге мы придем к следующему соотношению:
Нейрослой
Многие слышали о нашумевшей нейронной сети chatGPT от OpenAI, которая способна решать множество текстовых задач: отвечать на вопросы, генерировать идеи и т. д. ChatGPT успешно используется для программирования, так как умеет писать код, находить ошибки в коде, комментировать код и многое другое. Логично, что мы, как Университет искусственного интеллекта, специализируемся на искусственном интеллекте и, естественно, решили попробовать использовать chatGPT для генерации и обучения нейронных сетей и проведения экспериментов. У нас это получилось: chatGPT не только применима, но и значительно упрощает работу по генерации архитектуры нейронных сетей и проведению экспериментов. Ниже приводен пример решения задачи классификации водителей на внимательных, засыпающих, отвлекающихся на телефон и т. п. — классификация изображений на 10 классов. Типовая задача, решать ее мы будем комплексно, начиная с подготовки датасета, который необходим для обучения нейронных сетей. В нашем случае это zip-архив, который надо скачать, подготовить, превратить в обучающие выборки, нормировать и преобразовать перед тем, как подать в нейронную сеть. Часто это гораздо больший рутинный труд для разработчика, чем само обучение нейронной сети. Мы сделаем и то, и другое исключительно с помощью запросов к chatGPT — вся статья представляет собой только запросы в chatGPT, без единой строчки рукописного кода – это был важный критерий. Приводим успешные запросы и пару ошибок для примера. При работе с chatGPT важно уметь правильно задать системе запрос. Она умная и отлично понимает даже сложные комплексные запросы, но иногда человек формулирует запрос так, что системе он не понятен. Поэтому важный скил программирования с помощью chatGPT – промт-инжиниринг, умение правильно поставить задачу. Вообще работа с chatGPT похожа на работу с очень умным junior’ом, который знает всё, что только можно знать в Интернете, но некоторые задачи понимает не так, как нужно. И, повторяю, результат зависит от умения правильно поставить ему задачу. Важный момент: мы будем обсуждать машинное обучение, создание нейронных сетей, поэтому аудитория может разделиться на две части. Первая – те, кто разбирается в машинном обучении и в библиотеке Keras. Для них будут интересны и сами запросы к chatGPT, которые мы сформулировали, и код, который она создает. Вторая часть аудитории – это разработчики, которые не разбираются в машинном обучении и Keras. Скорее всего, им будет не совсем понятно, что происходит (что такое слои, что такое эпохи, что такое батчи), но будет интересен сам формат запросов к chatGPT и то, как мы их формулируем. Возможно, в дальнейшем вы захотите либо изучать машинное обучение, либо программировать с помощью chatGPT, и тогда вам будет полезно посмотреть, какие бывают запросы, как мы их переструктурируем в случае ошибки, даже если вы не до конца будете понимать код созданной нейронной сети на Keras. Рассказываем, как устроены наши данные и как их нужно подготовить для подачи в сеть.
Но ее легко получить путем увеличения количества нейронов. Давайте попробуем реализовать обучение с тремя нейронами в скрытом слое и одним выходным (выход ведь у нас только один). Чтобы все получилось, создадим массив X и Y, имеющий обучающие данные и саму нейронную сеть:
Пусть у нас уже есть нейронная сеть, но ведь ее ответы являются случайными, то есть наша нейросеть не обучена. Сейчас она способна лишь по входному вектору input выдавать случайный ответ, но нам нужны ответы, которые удовлетворяют конкретной поставленной задаче. Дабы этого достичь, сеть надо обучить. Здесь потребуется база тренировочных примеров и множество пар X — Y, на которых и будет происходить обучение, причем с использованием известного алгоритма обратного распространения ошибки.
Напиши подготовку тренировочных данных для нейронной сети по классификации изображений, для чего напиши код скачивания датасета при помощи gdown по этой ссылке и код разархивации датасета из zip-формата в директорию ‘content/cars’. Датасет не поделен на train и test. Он состоит из 10 папок (название каждой соответствует названию категории), каждый файл необходимо открыть при помощи Image.open, не используй генератор. Добавь в код масштабирование данных и приведи y_train к формату one hot encoding. Используй LabelEncoder для кодирования строковых меток в целые числа. Определи список названий классов и количество классов. Создай простую keras-модель на получившихся данных. Ответ chatGPT: import os import gdown import zipfile from PIL import Image from sklearn.preprocessing import LabelEncoder from sklearn.utils import shuffle from sklearn.model_selection import train_test_split import numpy as np from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense, Flatten, Conv2D, MaxPooling2D # Скачиваем датасет url = ‘ https://storage.yandexcloud.net/aiueducation/marketing/datasets/reality.zip' ; gdown.download(url, ‘reality.zip’, quiet= False ) # Распаковываем набор данных with zipfile.ZipFile( «reality.zip»,»r» ) as zip_ref: zip_ref.extractall( «content/cars» ) # Загружаем изображения и ярлыки X = [] y = [] classes = os.listdir( «content/cars» ) for i, class_name in enumerate ( classes ): for image_name in os.listdir( «content/cars/» + class_name): image = Image. open ( «content/cars/» + class_name + «/» + image_name) image = image.resize(( 64, 64 )) X.append(np.array(image)) y.append(class_name) # Масштабируем данные X = np.array(X) / 255.0 # Кодируем ярлыки label_encoder = LabelEncoder() y = label_encoder.fit_transform(y) # Приводим ярлыки к формату One-hot encoding num_classes = len(np.unique(y)) y = to_categorical(y, num_classes) # Перемешиваем данные X, y = shuffle(X, y) # Делим набор данных на тренировочную и тестовую выборки X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # Создаем простую модель keras model = Sequential() model.add(Conv2D( 32 , ( 3, 3 ), activation= ‘relu’ , input_shape=( 64, 64, 3 ))) model.add(MaxPooling2D(( 2, 2 ))) model.add(Flatten()) model.add(Dense(num_classes, activation= ‘softmax’ )) model. compile (optimizer= ‘adam’ , loss= ‘categorical_crossentropy’ ,
metrics=[ ‘accuracy’ ])
Последовательность нейрослоев часто применяют для более глубокого обучения нейронной сети и большей формализации имеющихся данных. Именно поэтому, чтобы получить итоговый выходной вектор, нужно проделать вышеописанную операцию пару раз подряд по направлению от одного слоя к другому. В результате для 1-го слоя входным вектором будет являться X, а для последующих входом будет выход предыдущего слоя. То есть нейронная сеть может выглядеть следующим образом:
Еще есть, например, метод обратного распространения ошибки — градиентный алгоритм для многослойных нейросетей. Сигналы ошибки, рассчитанные с помощью градиента, распространяются от выхода нейронной сети к входу, то есть идут не в прямом, а в обратном направлении.