Как ни крути, но языковая модель 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, регуляризации и увеличению периодов обучения обновленная модель должна иметь больше шансов научиться распознавать квадраты без переобучения.
Давайте проверим это в деле:

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

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