Делаем 10-минутную задачу за 2 часа с помощью ChatGPT

Задача стояла относительно тривиальная: нужно было сгенерировать look-up table (LUT) для по-канального увеличения контраста (S-кривая), и применить эту кривую на фотографии с камеры. Берем ChatGPT (даже 3.5 должен прокатить, тут всего-то одна формула), за 10 секунд получаем Python-код типичной S-кривой (также ChatGPT для моего удобства сразу добавил отрисовку графика через matplotlib), настраиваем параметр "кривизны" кривой по вкусу - и можно вставлять в финальное приложение. Среди множества S-кривых/сигмоид ChatGPT использовал "логистическую функцию" - она одна из наиболее простых. Но у неё есть и недостаток - параметром "кривизны" нельзя сменить увеличение контраста на снижение (и наоборот) - впрочем, мне это особо и не нужно было, снижать контраст в целевом применении не нужно никогда (крайний случай - оставить все как есть, а это логистическая функция может).
Но праздновать завершение работы оказалось рано: сгенерированный код упрямо рисовал график, снижающий контраст вместо его увеличения. Когда я предъявил эту претензию ChatGPT - он извинился, и с каждым разом выдавал все более и более поломанный код. Просто изменить параметр кривой оказалось невозможно т.к. формула недостаточно универсальная. Что-ж, похоже тут с ChatGPT ничего не получится - но это и не конец света. В конечном итоге, границы применимости LLM известны, и чем более узкая сфера - тем проще за них выйти. Но история тут не заканчивается.

Для справки, вот исходный (наилучший) вариант от ChatGPT (можно потренироваться и найти тут ошибки):
import numpy as np
import matplotlib.pyplot as plt
def create_s_curve_lut():
# Define parameters for the sigmoid curve
a = 10.0 # Adjust this parameter to control the curve's shape
b = 127.5 # Midpoint of the curve (127.5 for 8-bit grayscale)
# Create the S-curve LUT using the sigmoid function
lut = np.arange(256)
lut = 255 / (1 + np.exp(-a * (lut - b) / 255))
# Normalize the LUT to the 0-255 range
lut = (lut - np.min(lut)) / (np.max(lut) - np.min(lut)) * 255
return lut.astype(np.uint8)
# Create the S-curve LUT
s_curve_lut = create_s_curve_lut()
# Plot the S-curve for visualization
plt.plot(s_curve_lut, range(256))
plt.xlabel("Input Values (0-255)")
plt.ylabel("Output Values (0-255)")
plt.title("S-curve Contrast Enhancement LUT")
plt.show()
# You can access the S-curve LUT with s_curve_lut
В этот момент я поставил крест на LUT от ChatGPT и переделал (если так можно говорить про пару строчек кода из google) используя регуляризованную неполную бета-функцию. Покрутил параметры a=b пока не получил на графике кривую по вкусу (аналогично тому, что выставляю обычно руками в графическом редакторе) и наконец применил LUT на тестовое изображение используя в OpenCV. К моему полному удивлению, функция уменьшила контраст, а не увеличила его. Как так?
Медитирование не позволило быстро найти ошибку. Я написал тестовый кусочно-линейный LUT для увеличения контраста - и на изображении он дал ожидаемый результат. Лишь добавив кусочно-линейный LUT на график - корень проблемы стал виден: когда я выкинул функцию генерации S-curve LUT от ChatGPT, я оставил код рисования графика. Там ChatGPT старательно напечатал заголовок графика и названия осей.. но сделал конкретную подставу используя данные оси X - в Y, и наоборот. Т.к. параметры plt.plot используются без имени - не используя регулярно / не помня порядок параметров человеку очень легко тут не заметить ошибку.
Таким образом, когда я настраивал фактор формы кривой - я настраивал его на перевернутом графике, и сделал его уменьшающим контраст. Когда я обвинял ChatGPT в том, что он неправильную формулу мне сделал - и он соглашаясь со мной судорожно пытался что-то исправить - у него не было шансов. Т.к. формула была правильная, а ошибка была в графике. Конечно, если ChatGPT указать на ошибку в выводе графика - он радостно соглашается и исправляет её (но так я и сам могу). Это подстава уровня #define true false на университетских компьютерах.
Помню у моего преподавателя по аналитической геометрии был финальный босс на последнем экзамене: когда рассказываешь ему доказательство, он внезапно мог не согласится с одним из шагов и сказать что он неправильный, и чтобы получить 5 - нужно было не запаниковав - доказать верность своего решения. Надеюсь, когда-нибудь языковые модели смогут чаще не соглашаться с пользователем, который не всегда прав.
▶ Глянуть в код
Но и это оказался не конец: Взглянув на график - бросается в глаза легкая асимметричность кривой GPT-TRAP в районе 255. Это банальная ошибка округления : вычисленное вещественное число результата просто приводят к uint8 (=отбрасывают дробную часть), а значит результат (и яркость изображения) будет в среднем на 0.5 единицы / 0.25% меньше, пиксели с полной яркостью (255) будут намного более редкими. Интересной эту ошибку делает то, что она есть в коде, сгенерированной и ChatGPT, и Bing Copilot и Google Bard. Т.е. видимо такой код очень распространен в тренировочных данных и все модели решили что "умножить на 255 и привести к uint8" - это успех. Технически конечно в диапазон мы попали, но результат не безупречен.
▶ Глянуть в код
Выводы:
- Языковые модели - как junor разработчики: они могут и будут делать неожиданные ошибки, им нужны четкие инструкции и руководство. Впрочем, разница в том, что джуны вырастут, а у моделей остается ждать следующих поколений. Как и с джунами - с моделями нужно иметь реалистичные ожидания.
- Весь код от языковых моделей нужно проверять, никому верить нельзя. И чем менее истоптанная поляна, тем тщательнее нужно проверять. Языковые модели генерируют код очень похожий на правду, а значит ошибки могут быть очень дороги в исправлении.
- Если получаем неожиданный результат - зачастую проще спросить несколько разных моделей и сравнить результаты : сейчас в дополнение к ChatGPT (3.5/4) есть Copilot, Bard, Replit и другие. Никто из них не выдал идеальный код с первой попытки, но по этому пути мне не пришлось бы залипать с графиком.
- Некоторые ошибки - систематические для языковых моделей, видимо унаследованные из частично пересекающегося набора данных для обучения, где эти ошибки были широко представлены (т.к. языковые модели на текущий момент доверяют примерам для обучения безусловно, в отличие от людей). Т.е. модели пока не могут превзойти по качеству данные для обучения, и могут лишь к ним приблизится. Непонятно, сколько работы еще предстоит чтобы "превзойти учителя", может так оказаться, что это тот случай когда 10% работы занимают 90% времени. Но прогресс в последние 3 года движется стремительно, возможны и сюрпризы.