Как ни крути, но языковая модель ChatGPT никогда не может полностью заменить программиста, так как только около 1/10 от общего времени процесса разработки занимает написание кода. Однако ChatGPT великолепно помогает в различных аспектах программирования. Чем больше навыков и опыта у программиста, тем больше пользы он может извлечь от "помощника", который может:
Производить оптимизацию кода и улучшать производительность.
Искать и устранять ошибки в коде.
Объяснять сложные концепции и алгоритмы.
Помогать в разработке идеи и выборе подходящей архитектуры.
Создавать прототипы и демонстрационные версии программ.
Давать советы по стилю и лучшим практикам программирования.
Автоматизировать повторяющиеся задачи.
Генерировать код на основе спецификаций или заданных параметров.
Расширять функциональность с помощью плагинов и инструментов.
Писать документацию и комментарии к коду.
Сегодня невероятно глупо не использовать возможности ChatGPT. Это действительно универсальный помощник, который существенно облегчает жизнь программиста и повышает эффективность разработки. Такое программирование становится гораздо более приятным и эффективным делом, чем когда-либо раньше.
Помощник для программиста
Возьмем не простой пример, которым завален весь интернет, а возьмем задачу поинтереснее: используя помощника ChatGPT напишем код, который используя нейросеть сможет решать задачу по распознаванию квадратов на картинке.
Спросим нейросеть об этапах создания такого кода:
- Из каких этапов состоит код для распознавания квадратов на картинке с помощью нейросети?
Из каких этапов состоит код для распознавания квадратов на картинке с помощью нейросети?

Итак, мы получили этапы, которые должны закодировать:
Код для распознавания квадратов на картинке с помощью нейросети обычно состоит из следующих этапов:
Подготовка данных: Этот этап включает загрузку изображений, преобразование их в формат, совместимый с нейросетью, и разделение данных на обучающую и тестовую выборки.
Создание модели нейросети: Здесь определяется архитектура нейросети, включая типы слоев, количество нейронов в каждом слое, функции активации и другие параметры модели.
Обучение модели: На этом этапе модель нейросети обучается на обучающей выборке. Она подстраивает свои веса и параметры, чтобы минимизировать ошибку предсказания. Обычно используется метод градиентного спуска для обновления весов.
Оценка модели: После завершения обучения модели оценивают на тестовой выборке, чтобы оценить ее производительность. Возможны различные метрики, такие как точность, полнота и F-мера, чтобы оценить эффективность распознавания квадратов.
Применение модели: После успешной оценки модель можно применять для распознавания квадратов на новых изображениях. Это включает прогон изображений через модель и получение предсказанных меток или координат квадратов на изображении.
Оптимизация и доработка: При необходимости можно производить оптимизацию модели, включая изменение ее архитектуры, параметров обучения и дополнительную настройку для достижения лучшей производительности.
Начнем последовательно писать код по пунктам.
1. Подготовка данных
Сначала мы должны создать картинки с квадратами (датасет для обучения нейросети), так и пишем ей:
напиши код на python генерации картинок 128х128 с квадратом в центре, разных размеров и разных цветов

P.S. Далее я уже не буду прикладывать скриншоты экрана, а только код с запросами и ответами нейросети.
Итак, мы получили код для генерации одной картинки.
from PIL import Image, ImageDraw import random # Размеры изображения image_width = 128 image_height = 128 # Создаем новое изображение image = Image.new("RGB", (image_width, image_height), "white") draw = ImageDraw.Draw(image) # Генерация квадрата square_size = random.randint(10, 80) # Случайный размер квадрата от 10 до 80 пикселей square_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Случайный цвет квадрата # Координаты квадрата в центре изображения left = (image_width - square_size) // 2 top = (image_height - square_size) // 2 right = left + square_size bottom = top + square_size # Рисуем квадрат draw.rectangle([left, top, right, bottom], fill=square_color) # Сохраняем изображение image.save("generated_image.png")
Запуск этого кода генерит такую картинку:

Круто! Но нам нужен датасет для обучения будущей нейросети, поэтому изменим код сами - добавив цикл, или попросим сделать это нейросеть ChatGPT:
Делаем новый запрос:
измени код выше добавив в него цикл для генерации 100 изображений, которые должны сохраняться в подкаталог "boxes" каталога "training_dataset" с именами картинок генерирующихся циклом от 0 до 100 с расширением png
Получаем ответ:
from PIL import Image, ImageDraw import random import os # Размеры изображения image_width = 128 image_height = 128 # Создаем директорию для сохранения изображений output_dir = "training_dataset/boxes" os.makedirs(output_dir, exist_ok=True) # Цикл для генерации 100 изображений for i in range(100): # Создаем новое изображение image = Image.new("RGB", (image_width, image_height), "white") draw = ImageDraw.Draw(image) # Генерация квадрата square_size = random.randint(10, 80) # Случайный размер квадрата от 10 до 80 пикселей square_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) # Случайный цвет квадрата # Координаты квадрата в центре изображения left = (image_width - square_size) // 2 top = (image_height - square_size) // 2 right = left + square_size bottom = top + square_size # Рисуем квадрат draw.rectangle([left, top, right, bottom], fill=square_color) # Сохраняем изображение image_name = f"image_{i}.png" image_path = os.path.join(output_dir, image_name) image.save(image_path)
Ответ ChatGPT: В этом обновленном коде добавлен цикл, который выполняется 100 раз, генерируя 100 изображений с квадратами разных размеров и цветов. Изображения сохраняются в подкаталог "boxes" внутри каталога "training_dataset". Имена файлов генерируются в формате "image_0.png", "image_1.png" и так далее, в зависимости от номера итерации цикла.
Я уже в шоке! Вот результат запуска кода:

Теперь у нас есть загруженные(сгенерированные) изображения, преобразованные в формат совместимый с нейросетью - т.е. в формате png (можно использовать другой формат) и все одинакового размера с нужными нам объектами, причем все они сохранены в каталог training_dataset/boxes. Причем эти каталоги создаются автоматом, если их нет.
Для разделения датасета, для обучения нейросети распознаванию квадратов, на обучающую и тестовую выборки, логично написать запрос
используя библиотеку tensorflow напиши команды для загрузки этих изображений из каталога "training_dataset" для обучающей и тестовой выборки
P.S. почему я указал каталог "training_dataset", а не каталог "training_dataset/boxes"?
- Т.к. каталог boxes - фактически играет роль маркера одного класса картинок находящихся в нем.
# pip install "tensorflow<2.11" from tensorflow.keras.preprocessing.image import ImageDataGenerator # Путь к каталогу с изображениями image_dir = "training_dataset" # Создание генератора изображений для обучающей выборки train_data_gen = ImageDataGenerator(rescale=1./255, validation_split=0.2) # Загрузка обучающей выборки train_generator = train_data_gen.flow_from_directory( image_dir, target_size=(128, 128), batch_size=32, class_mode=None, subset="training" ) # Загрузка валидационной выборки validation_generator = train_data_gen.flow_from_directory( image_dir, target_size=(128, 128), batch_size=32, class_mode=None, subset="validation" )
Запускаем код:

Видим, что успешно загрузили картинки класса boxes, из них 80 отдали обучающей выборке и 20 для проверочного набора.
Немного поправим код, добавим перемешивание наших картинок:
добавь в код выше перемешивание картинок
Нейросеть взяла и кардинально поменяла код, выдав такой результат:
Вот обновленный код с перемешиванием изображений в каждой эпохе для обучающей выборки:
import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator import numpy as np # Путь к каталогу с изображениями image_dir = "training_dataset/boxes" # Загрузка изображений в виде массива numpy images = tf.keras.preprocessing.image_dataset_from_directory( image_dir, image_size=(128, 128), batch_size=32, shuffle=True, seed=42, validation_split=0.2, subset="training" ) # Перемешивание данных images = images.unbatch() images = np.array([img for img, _ in images]) np.random.shuffle(images) # Разделение на обучающую и валидационную выборки train_size = int(0.8 * len(images)) train_images = images[:train_size] val_images = images[train_size:]
В этом обновленном коде мы используем функцию
image_dataset_from_directoryиз TensorFlow для загрузки изображений в виде набора данных. Мы устанавливаем параметрshuffle=True, чтобы перемешать изображения в каждой эпохе. Параметрseed=42используется для фиксации случайного перемешивания, чтобы результат был воспроизводимым.Затем мы преобразуем набор данных в отдельные изображения, чтобы выполнить перемешивание вручную. Мы используем функцию
unbatchдля этого. После этого мы перемешиваем массив изображений с помощьюnp.random.shuffle.Наконец, мы разделяем перемешанный массив на обучающую и валидационную выборки, используя индекс
train_size. В результате у нас будет переменнаяtrain_images, содержащая обучающие изображения, и переменнаяval_images, содержащая валидационные изображения.
Причем код рабочий, вот результат его выполнения:

Считаю, что нужно сделать более логично, такой командой:
используя библиотеку tensorflow напиши команды для загрузки этих изображений из каталога "training_dataset" для обучающей и тестовой выборки, не используя numpy, с перемешиванием изображений в обучающей и тестовой выборке
Получаем упрощенный ответ:
Вот пример кода для загрузки изображений из каталога "training_dataset/boxes" в TensorFlow для обучающей и тестовой выборки с перемешиванием:
import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator # Путь к каталогу с изображениями image_dir = "training_dataset/boxes" # Создание генератора изображений для обучающей и тестовой выборки data_gen = ImageDataGenerator(rescale=1./255, validation_split=0.2) # Загрузка обучающей выборки train_data = data_gen.flow_from_directory( image_dir, target_size=(128, 128), batch_size=32, class_mode=None, subset="training", shuffle=True, seed=42 ) # Загрузка тестовой выборки test_data = data_gen.flow_from_directory( image_dir, target_size=(128, 128), batch_size=32, class_mode=None, subset="validation", shuffle=True, seed=42 )
В этом коде мы используем
ImageDataGeneratorдля создания генератора изображений. Опцияrescale=1./255выполняет масштабирование значений пикселей в диапазоне от 0 до 1. Опцияvalidation_split=0.2указывает, что 20% данных будет использоваться для валидации.Затем мы используем метод
flow_from_directoryдля загрузки изображений из каталога. Мы указываемtarget_size=(128, 128), чтобы изменить размер изображений до 128x128 пикселей.batch_sizeопределяет размер пакета данных, аclass_mode=Noneуказывает, что у нас нет категориальных меток для классификации.Обучающая и тестовая выборки загружаются с помощью
subset="training"иsubset="validation"соответственно. Параметрыshuffle=Trueиseed=42используются для перемешивания данных в обеих выборках. Это гарантирует, что изображения будут перемешаны одинаковым образом в каждой выборке при повторном запуске кода.
Такой код меня уже устраивает, но нужно проверить, поэтому его запускаем:

Теперь Ок, можем двигаться дальше.
2. Создание модели нейросети
Пишем запрос)) пок�� практически всё сделали за нас...
Для кода выше создай модель нейросети для её обучения распознавания квадратов на этих обучающих и тестовых выборках
Получаем ответ:
Вот пример кода для создания модели нейросети для обучения распознавания квадратов на обучающих и тестовых выборках:
import tensorflow as tf # Создание модели нейросети model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) # Компиляция модели model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Обучение модели model.fit(train_data, epochs=10, validation_data=test_data)
В этом коде мы используем модуль
tf.kerasдля создания модели нейросети. Мы определяем модель как последовательную сеть с несколькими сверточными слоями и плотными слоями.В нашем примере, мы используем три сверточных слоя
Conv2Dс активациейreluи пулингомMaxPooling2Dдля уменьшения размерности данных. Затем мы выпрямляем данные с помощью слояFlattenи добавляем два плотных слояDenseс активациейrelu. Наконец, мы добавляем выходной слойDenseс активациейsigmoid, так как мы хотим решить задачу бинарной классификации - определение присутствия или отсутствия квадрата.Модель компилируется с оптимизатором
adam, функцией потерьbinary_crossentropyдля бинарной классификации и метрикойaccuracyдля оценки производительности модели.Затем мы обучаем модель с помощью метода
fit, используя обучающую выборкуtrain_dataи валидационную выборкуtest_dataиз предыдущего кода. Мы указываемepochs=10, чтобы обучение выполнилось в течение 10 эпох.
Запустим код, добавив в него из прошлого кода загрузку обучающей и валидационной выборки:

Наконец-то получаем ошибку: ValueError: Target data is missing. Your model was compiled with loss=binary_crossentropy, and therefore expects target data to be provided in fit().
Спросим об этом нейросеть:
исправь ошибку ValueError: Target data is missing. Your model was compiled with loss=binary_crossentropy, and therefore expects target data to be provided in `fit()`
её ответ:
Извините за путаницу. Ошибка возникает из-за того, что модель ожидает, чтобы целевые данные были предоставлены при обучении, но в предыдущем коде не было указано, какие целевые данные использовать. Если мы предполагаем, что квадраты должны быть распознаны как положительные примеры, то мы можем создать соответствующие метки целевых данных для обучающей и тестовой выборок.
Вот обновленный код, исправляющий эту проблему:
import tensorflow as tf # Создание модели нейросети model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) # Компиляция модели model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Создание целевых данных для обучающей выборки train_labels = tf.concat([tf.ones((len(train_data.filepaths))), tf.zeros((len(train_data.filepaths)))], axis=0) # Создание целевых данных для тестовой выборки test_labels = tf.concat([tf.ones((len(test_data.filepaths))), tf.zeros((len(test_data.filepaths)))], axis=0) # Обучение модели model.fit(train_data, train_labels, epochs=10, validation_data=(test_data, test_labels))
В этом обновленном коде мы создаем метки целевых данных для обучающей и тестовой выборок. Мы используем функцию
tf.concatдля объединения тензоров из единиц (положительные примеры) и нулей (отрицательные примеры) в соответствующих пропорциях. Затем мы передаем эти метки целевых данных в методfitвместе с обучающей и тестовой выборками для выполнения обучения модели.Теперь модель будет корректно обучаться на задаче бинарной классификации для распознавания квадратов.
Запускаем и проверяем!
Опять ошибка: ValueError: y argument is not supported when using keras.utils.Sequence as input.

y argument is not supported when using keras.utils.Sequence as input.Давайте и эту ошибку исправим с помощью ChatGPT:
исправь ошибку ValueError: `y` argument is not supported when using `keras.utils.Sequence` as input.
Получаем ответ и новый код:
При использовании объекта
ImageDataGenerator.flow_from_directoryс параметромclass_mode=None, модель ожидает, что данные будут представлены в виде генератора типаkeras.utils.Sequence, а не массива.Для решения этой проблемы, мы можем создать собственный класс-последовательность (sequence) для предоставления данных модели. Вот исправленный код:
import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.utils import Sequence import numpy as np # Путь к каталогу с изображениями image_dir = "training_dataset/boxes" # Создаем собственный класс-последовательность (Sequence) class ImageSequence(Sequence): def __init__(self, image_dir, batch_size, shuffle=True): self.image_generator = ImageDataGenerator(rescale=1./255, validation_split=0.2) self.image_data = self.image_generator.flow_from_directory( image_dir, target_size=(128, 128), batch_size=batch_size, class_mode='binary', subset="training", shuffle=shuffle, seed=42 ) self.batch_size = batch_size def __len__(self): return len(self.image_data) def __getitem__(self, idx): batch_x, batch_y = self.image_data[idx] return batch_x, batch_y # Создание обучающей и тестовой последовательностей train_seq = ImageSequence(image_dir, batch_size=32) test_seq = ImageSequence(image_dir, batch_size=32, shuffle=False) # Создание модели нейросети model = tf.keras.models.Sequential([ tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(64, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Conv2D(128, (3, 3), activation='relu'), tf.keras.layers.MaxPooling2D((2, 2)), tf.keras.layers.Flatten(), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) # Компиляция модели model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Обучение модели model.fit(train_seq, epochs=10, validation_data=test_seq)
В этом коде мы создаем класс
ImageSequence, который наследуется отSequence. В методе__getitem__мы возвращаем пакеты данных и меток целевых данных. В методе__len__мы возвращаем общее количество пакетов данных.Затем мы создаем обучающую и тестовую последовательности, используя этот класс. Мы передаем каталог с изображениями и размер пакета данных в качестве параметров.
После этого создается модель нейросети, компилируется и обучается на обучающей и тестовой последовательностях.
Обратите внимание какие везде логические объяснения, хотя прошлый код мы могли исправить вот здесь закомментировав строки запрещающие "класс".

Ок, давайте запустим итоговый код, который создала нейросеть:

Вот он момент истины:
Код получился условно "рабочий", т.к. код датасеты генерит +1, делит и загружает обучающие выборки +1, модель создает +1, обучает +1 - НО модель не сходится -1 ! т.е. идет переобучение модели и поэтому такая модель не особо пригодна.
Следовательно нам уже нужно "руками" доработать только модель нейросети.
Или )) попросить ChatGPT сделать это:
модель нейросети не сходится, происходит переобучение, поменяй код, так чтобы нейросеть смогла обучиться распознавать квадраты
Вот ответ:
Чтобы бороться с проблемой переобучения и улучшить обобщающую способность модели, можно применить следующие изменения в коде:
Добавить слои Dropout после сверточных слоев и перед плотными слоями. Dropout помогает предотвратить переобучение, случайным образом обнуляя выходные значения некоторых нейронов во время обучения.
Использовать регуляризацию L2 для плотных слоев. Это позволяет снизить сложность модели и ограничить веса нейронов, что помогает предотвратить переобучение.
Увеличить количество обучающих данных. Добавить больше изображений с квадратами в обучающую выборку поможет модели лучше обобщить паттерны, что снизит вероятность переобучения.
Уменьшить размер пакета данных (
batch_size), чтобы увеличить степень стохастичности в процессе обучения.Увеличить количество эпох обучения для длительного обучения модели.
Вот обновленный код, учитывающий эти изменения:
import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout from tensorflow.keras.regularizers import l2 # Путь к каталогу с изображениями image_dir = "training_dataset" # Создаем собственный класс-последовательность (Sequence) class ImageSequence(tf.keras.utils.Sequence): def __init__(self, image_dir, batch_size, shuffle=True): self.image_generator = ImageDataGenerator(rescale=1./255, validation_split=0.2) self.image_data = self.image_generator.flow_from_directory( image_dir, target_size=(128, 128), batch_size=batch_size, class_mode='binary', subset="training", shuffle=shuffle, seed=42 ) self.batch_size = batch_size def __len__(self): return len(self.image_data) def __getitem__(self, idx): batch_x, batch_y = self.image_data[idx] return batch_x, batch_y # Создание обучающей и тестовой последовательностей train_seq = ImageSequence(image_dir, batch_size=32) test_seq = ImageSequence(image_dir, batch_size=32, shuffle=False) # Создание модели нейросети model = tf.keras.models.Sequential([ Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)), MaxPooling2D((2, 2)), Dropout(0.25), Conv2D(64, (3, 3), activation='relu'), MaxPooling2D((2, 2)), Dropout(0.25), Conv2D(128, (3, 3), activation='relu'), MaxPooling2D(2, 2), Dropout(0.25), Flatten(), Dense(128, activation='relu', kernel_regularizer=l2(0.001)), Dropout(0.5), Dense(1, activation='sigmoid') ]) # Compilation model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # Training model.fit(train_seq, epochs=20, validation_data=test_seq) # Evaluate on the test set model.evaluate(test_seq)
В этом обновленном коде мы добавили Dropout слои после каждого сверточного слоя и перед Conv2D слоями. Мы установили коэффициент отсева равным 0,25, чтобы случайным образом снизить 25% выходной мощности нейронов во время обучения.
Мы также добавили регуляризацию L2 (kernel_regularizer=l2(0.001)) к слою Dense с параметром регуляризации, равным 0.001. Это помогает контролировать сложность модели и предотвращает переобучение.
Модель скомпилирована и обучена с использованием функции оптимизатора. Мы увеличили количество эпох до 20 для более длительного обучения. Наконец, мы оцениваем модель на тестовом наборе с помощью функции evaluate.
Благодаря включению Dropout, регуляризации и увеличению периодов обучения обновленная модель должна иметь больше шансов научиться распознавать квадраты без переобучения.
Давайте проверим это в деле:

Опять не то ....
А теперь делаем небольшую магию) Меняем всего лишь модель нейросети:

И получаем уже сходимость сети.
Т.е. в целом можно пользовать нейрос��ть как хорошего помощника в написании некоторого драфт кода, но требуется дорабатывать этот код. И иногда большой объем работ именно такая нейросеть пишет быстро и почти не надо правок. А вот там, где требуется некоторая экспертность - там нужно вручную править существенные моменты.
