Давайте воспользуемся задачей рисования сплайна в JS, чтобы увидеть, сможет ли ИИ победить людей.

· И. Процесс
0. Настройка: Данные
1. Исходный уровень
2. Как нарисовать однолинейный сплайн
3. Как нарисовать сплайн разной ширины?
4. Человеческий интеллект спешит на помощь: решение
5. ChatGPT очень хорошо преобразовывал код
6. Человеческая интуиция вам в помощь: Нарисуйте 3 сплайна
7. Заполните поле: Другая проблема
8. Окончательное решение
· II. Анализ
· Заключение
· Справочник

Математическое определение сплайна — это специальная «функция, определяемая кусочно с помощью многочленов». В информатике и компьютерной графике это кривая линия, основанная на интерполяциях. С точки зрения непрофессионала, это просто изогнутая линия.

Зачем нужно рисовать сплайн? Сплайны представляют интерес для исследователей ИИ, которые хотят обучить модель автономному вождению, потому что автомобильные полосы в основном представляют собой сплайны.

Мой интерес к рисованию сплайнов также связан с необходимостью обработки наборов данных для автономного вождения. Мне нужен был способ отображения данных сплайна на веб-странице, поэтому я искал решение в Javascript.

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

Я также использую это как пример того, что ИИ может и чего не может делать. Поскольку я использовал ChatGPT, вы можете воспринимать это как поучительную историю о том, как использовать ИИ в своей работе.

I. Процесс

0. Настройка: данные

Вот как выглядят наши данные:

const points = [
    { x: 0, y: 80, r: 2 },
    { x: 100, y: 100, r: 4 },
    { x: 200, y: 30, r: 6 },
    { x: 300, y: 50, r: 8 },
    { x: 400, y: 40, r: 10 },
    { x: 500, y: 80, r: 12 },
];

Как правило, данные сплайна состоят из списка «контрольных точек», каждая из которых представляет координаты x и y, как вы можете ожидать, а также «r», что означает «ширину» сплайна в каждой конкретной контрольной точке.

Наша задача — нарисовать непрерывный криволинейный сплайн, соединяющий все контрольные точки с разной шириной.

1. Базовый уровень

Давайте сначала соединим все контрольные точки прямыми линиями, чтобы сформировать базовую линию. В Javascript вы можете использовать библиотеку D3.

const line = d3.line()
    .x((d) => d.x)
    .y((d) => d.y);

svg.append('path')
    .datum(data)
    .attr('d', line)
    .attr('fill', 'none')
    .attr('stroke', 'green')
    .attr('stroke-width', (d) => d.r);

2. Как нарисовать однолинейный сплайн

Чтобы превратить прямые линии в сплайны, вам просто нужно указать форму линии: .curve(d3.curveCardinal). Вот полный код в отдельном файле HTML. Сохраните его и откройте в браузере, чтобы увидеть сплайн.

<html>
<head>
  <script src="https://d3js.org/d3.v7.min.js"></script>
  <style>
    .graph-container {
      width: 100%;
      height: 100px;
      position: relative;
    }
  </style>
</head>
<body>
    <div id="graph-container" class="graph-container"></div>
    <script>
        // Define the data points for the spline curve
        const data = [
            { x: 0, y: 80, r: 2 },
            { x: 100, y: 100, r: 4 },
            { x: 200, y: 30, r: 6 },
            { x: 300, y: 50, r: 8 },
            { x: 400, y: 40, r: 10 },
            { x: 500, y: 80, r: 12 },
        ];

        // Set up the SVG container
        const svgWidth = 600;
        const svgHeight = 200;
        const svg = d3.select('#graph-container')  // Select the graph-container div
            .append('svg')
            .attr('width', svgWidth)
            .attr('height', svgHeight);

        // Create the spline generator
        const line = d3.line()
            .x((d) => d.x)
            .y((d) => d.y)
            .curve(d3.curveCardinal);

        // Draw the spline curve
        svg.append('path')
            .datum(data)
            .attr('d', line)
            .attr('fill', 'none')
            .attr('stroke', 'green')
            .attr('stroke-width', (d) => d.r);
    </script>
</body>
</html>

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

3. Как нарисовать сплайн разной ширины?

Однако есть небольшая проблема с первой попыткой. Если вы заметили, ширина сплайна везде одинаковая. Это не то, чего мы хотели. Гладкость сплайна есть, но требования к различной ширине не выполняются. Поэтому я снова спросил ChatGPT.

Результат был весьма разочаровывающим. Он выдал несколько неправильных ответов, большинство из которых даже не дало никаких результатов. Я застрял на некоторое время. К сожалению, ChatGPT не смог предложить мне новаторский подход, пока я не предложил разделить сплайн на несколько сегментов и позже соединить их в один сплайн.

Как только ChatGPT сказали, что делать, он быстро реализовал решение, добавив несколько сегментов линии поверх существующего сплайна:

// Draw line segments between control points with varying widths
for (let i = 1; i < data.length; i++) {
    const prevPoint = data[i - 1];
    const currPoint = data[i];

    svg.append('line')
        .attr('x1', prevPoint.x)
        .attr('y1', prevPoint.y)
        .attr('x2', currPoint.x)
        .attr('y2', currPoint.y)
        .attr('stroke', 'green')
        .attr('stroke-width', currPoint.r);
}

Это было многообещающе, поэтому я попытался направить его, чтобы дать мне решение, которое я искал. Увы, он продолжал давать мне подобные решения снова и снова. Иногда он неоднократно давал мне один и тот же ответ, пока я не просил его не делать этого.

Ясно, что это никуда не шло. Я застрял, как и ИИ.

4. Человеческий интеллект спешит на помощь: решение

Чтобы найти решение, я прибегнул к коллективному человеческому разуму. Я задал вопрос в Stackoverflow. Через пару дней мне дали несколько решений.

Первый ответ был похож на мой подход, за исключением того, что он делил каждый сегмент линии на еще более мелкие подсегменты.

for (let len = 0; len <= total; len += step) {
  const fromLen = Math.max(0, len - step);
  const toLen = Math.min(len + step, total);
  const point = path.node().getPointAtLength(len);
  const r = rValueByX(point.x);
  const from = path.node().getPointAtLength(fromLen);
  const to = path.node().getPointAtLength(toLen);
  
  svg.append('line')
    .attr('x1', from.x)
    .attr('y1', from.y)
    .attr('x2', to.x)
    .attr('y2', to.y)
    .attr('stroke-width', r)
    .attr('stroke', 'green');
}

Подход сработал хорошо, и вы можете увидеть результат ниже.

Теперь все, что мне нужно было сделать, это применить его в моем случае.

5. ChatGPT очень хорошо конвертировал код

Другим моим ограничением было то, что мне нужно было сделать это с помощью FabricJS, одной из популярных библиотек рендеринга.

Рисование простого сплайна не составило труда для ChatGPT. В течение нескольких минут он преобразовал код D3 в FabricJS, и на холсте был нарисован сплайн.

Чтобы сделать изогнутый сплайн, он использовал синтаксис строки пути. Для наших целей нам достаточно знать три команды:

  • M: означает «Переместить» и перемещается в точку
  • C: рисует «кубическую кривую Безье», соединяя 3 точки.
  • L: обозначает «линию», соединяющую две точки прямой линией.

Ниже приведен фрагмент кода, реализованный ChatGPT с использованием двух дополнительных интерполированных контрольных точек (controlPointX1 и controlPointX2).

     let pathString = '';
     const firstPoint = new fabric.Point(points[0].x, points[0].y);

     pathString += `M${firstPoint.x},${firstPoint.y}`;

     for (let i = 1; i < points.length; i++) {
      const prevPoint = points[i - 1];
      const currPoint = points[i];
      const strokeWidth = prevPoint.r;

      const controlPointX1 = (prevPoint.x + currPoint.x) / 2;
      const controlPointY1 = prevPoint.y;

      const controlPointX2 = controlPointX1;
      const controlPointY2 = currPoint.y;

      pathString += ` C${controlPointX1},${controlPointY1} ${controlPointX2},${controlPointY2} ${currPoint.x},${currPoint.y}`;
     }

Нарисован сплайн, но, как и в нашей первой попытке с D3, ширина остается неизменной. Проблема в том, что мы не можем использовать один и тот же код D3. Одним из ключей к решению задачи была возможность получить точку на определенной длине. Критическая строка была такой:

const point = path.node().getPointAtLength(len);

Это было возможно для пути D3, но не для пути FabricJS. Прямого способа получить информацию о точке на заданном расстоянии не было.

Я снова попросил ChatGPT решить эту проблему. Он очень старался предложить различные способы, но ни один из них не работал. Я снова застрял, как и ИИ.

6. Человеческая интуиция вам в помощь: Нарисуйте 3 сплайна

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

Идея была проста и легка в реализации. Мне даже не понадобился ChatGPT для реализации кода. Вот фрагмент кода:

    const pathStrings = [];
    const innerPathStrings = [];
    const outerPathStrings = [];

    for (let i = 0; i < points.length; i++) {
      const currPoint = points[i];
      const currWidth = currPoint.r;

      const innerControlPoint = new fabric.Point(currPoint.x, currPoint.y - currWidth);
      const outerControlPoint = new fabric.Point(currPoint.x, currPoint.y + currWidth);

      if (i === 0) {
        pathStrings.push(`M${currPoint.x},${currPoint.y}`);
        innerPathStrings.push(`M${innerControlPoint.x},${innerControlPoint.y}`);
        outerPathStrings.push(`M${outerControlPoint.x},${outerControlPoint.y}`);
      } else {
        const prevPoint = points[i - 1];
        const prevWidth = prevPoint.r;

        const controlPointX1 = (prevPoint.x + currPoint.x) / 2;
        const controlPointY1 = prevPoint.y;

        const controlPointX2 = controlPointX1;
        const controlPointY2 = currPoint.y;

        pathStrings.push(`C${controlPointX1},${controlPointY1} ${controlPointX2},${controlPointY2} ${currPoint.x},${currPoint.y}`);
        innerPathStrings.push(`C${controlPointX1},${controlPointY1 - prevWidth} ${controlPointX2},${controlPointY2 - currWidth} ${currPoint.x},${currPoint.y - currWidth}`);
        outerPathStrings.push(`C${controlPointX1},${controlPointY1 + prevWidth} ${controlPointX2},${controlPointY2 + currWidth} ${currPoint.x},${currPoint.y + currWidth}`);
      }
    }

    const pathString = pathStrings.join(" ");
    const innerPathString = innerPathStrings.join(" ");
    const outerPathString = outerPathStrings.join(" ");

Результат был ожидаемым:

7. Заполните поле: Еще одна проблема

Увидев успех своей интуиции, в моей голове вспыхнула лампочка: «Почему бы не заполнить область между внутренним и внешним путем цветом?» Это приведет к тому же результату, что и рисование сплайна разной ширины».

Чтобы заставить его работать, я снова использовал ChatGPT, чтобы соединить внутренний и внешний пути. После некоторой возни я обнаружил, что мне нужно изменить внешний путь, чтобы соединить внутренний путь. Код стал немного сложным, поэтому я не буду воспроизводить его здесь. Пожалуйста, обратитесь ко всему коду здесь для справки.

Но результат оказался не таким, как я ожидал. Вместо красивой закрытой области, соединив внутренний и внешний путь, я получил это:

Я был озадачен. Сначала я подумал, что ChatGPT ошибся. Первоначально это было так, но даже после исправления всех ошибок, чтобы строка пути области выглядела именно так, как я ожидал, результирующий путь области не заполнял область между внутренним и внешним путем, как я ожидал.

Оказалось, что есть неотъемлемый артефакт с конструкцией fabric JS path. Если вы заметили, часть строки внутреннего пути пути области выглядит правильно, но путь перевернутой внешней строки совершенно дурацкий, и именно в этом проблема.

Чтобы проиллюстрировать, что я имею в виду, вот две строки пути, которые должны были привести к одному и тому же сплайну, поскольку они проходят через одни и те же контрольные точки. Разница только в направлении.

const testString1 = "M50,60 C50,60 50,96 100,96 C150,96 150,24 200,24";
const testString2 = "M200,24 C200,24 150,24 150,96 C100,96 50,96 50,60";

Полученные формы выглядят совсем не так.

Оказывается, направление имеет большое значение для строк пути, поэтому то, что мы наблюдаем, соответствует ожидаемому. ChatGPT быстро подтвердил мои выводы, и я смог сократить время на поиск причины расхождений.

8. Окончательное решение

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

Для этого я не могу позволить Fabric JS выполнять интерполяции между контрольными точками. Вместо этого мне пришлось вычислять каждую точку на сплайне самостоятельно. Как только я узнал, что нужно сделать, я смог использовать ChatGPT, чтобы пройти последнюю милю. В результате получился этот потрясающе красивый сплайн в Fabric JS:

II. Анализ

Случайно история с рисованием сплайна закончилась противопоставлением человеческого интеллекта искусственному интеллекту.

Сначала я пытался сделать это самостоятельно, но ничего не вышло. Когда я использовал ChatGPT, я внезапно обрел сверхчеловеческие способности. Как я уже говорил, я полный новичок в Javascript, поэтому для меня написать любой код, работающий на этом языке, было само по себе подвигом.

Но я быстро столкнулся с препятствием, когда попытался сделать что-то немного другое «в глуши» (например, нарисовать сплайн с разной шириной), ChatGPT совсем не помог. Я решил, что причина в том, что не было достаточно данных для обучения, которые включали бы то, чего я хотел достичь.

Чтобы преодолеть препятствие, я прибегнул к традиционному коллективному человеческому разуму и интуиции. Только тогда я смог прийти к окончательному решению.

История показала мне как сильные, так и слабые стороны ChatGPT. Вот мой анализ:

Плюсы:

  1. Конверсии. ChatGPT особенно хорош для преобразования кода. От D3 до Fabric JS, от Python до Javascript и т.д.
  2. Начало работы. Со всеми примерами кода GitHub ChatGPT видел все это и делал все это с точки зрения популярных задач программирования.
  3. Внедрение алгоритмов. Если вы точно знаете, как это должно быть реализовано, ChatGPT станет для вас идеальным инструментом. Это может сэкономить вам много усилий по вводу текста, исследованию и отладке.

Минусы:

  1. Отсутствие правды и точности. Как уже сообщали многие люди, «галлюцинации» реальны. ChatGPT может рассказать миллион правдоподобных историй, которые не являются правдой, точными или даже точными. Он распространяет много слухов в Интернете, но вы не знаете, насколько вы должны доверять его ответам.
  2. Недостаток новаторских идей. ChatGPT действует как младший разработчик, который достаточно опытен, чтобы выполнять любые кодовые работы, но не может дать вам много информации или инновационных идей, которые вы не пробовали раньше.
  3. Недостаток саморефлексии: некоторые люди считают, что LLM, такие как ChatGPT, уже стали «разумными», моя встреча до сих пор подтверждает, что они вообще далеки от того, чтобы быть разумными или сознательными. Одним из важнейших аспектов сознания является способность к самоанализу своего состояния. Другими словами, мы, как разумные существа, можем наблюдать за тем, что мы делаем, и при необходимости вносить исправления. ChatGPT делает это до определенной степени, когда пользователь указывает, что его ответы неверны. Однако повторяющиеся неправильные ответы в одном и том же направлении являются явным признаком того, что ChatGPT понятия не имеет, что делает.

Заключение

Так кто победил? Человеческий интеллект или искусственный интеллект? Основываясь на анализе, я бы сказал, что коллективный человеческий интеллект (как вы видели, мой собственный интеллект не мог даже приступить к выполнению простой задачи) по-прежнему имеет преимущество перед ИИ.

Вопрос в том, как долго человеческий интеллект сможет удерживать свое преимущество. Я боюсь, что с нынешними темпами развития ИИ разрыв сокращается очень быстро, прежде чем мы даже осознаем это.

Однако настоящим победителем является ИИ, как в «дополненном интеллекте». Окончательное решение было найдено не тогда, когда человеческий интеллект был противопоставлен искусственному интеллекту, а когда оба использовались вместе: то есть человеческий интеллект вместе с ИИ, приправленным интуицией.

Сочетание человеческого и искусственного интеллекта кажется идеальным рецептом для решения сложных проблем, с которыми мы сталкиваемся. Это дает мне блестящую надежду на беспрецедентную новую эру ИИ.

Ссылка

  1. Посмотреть все примеры кода, использованные в статье, можно здесь.

СТАНЬТЕ ПИСАТЕЛЕМ на MLearning.ai //БЕСПЛАТНЫЕ инструменты ML// Divine Code