Время прочтения: 11 мин.

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

Мы познакомим вас с двумя моделями‑помощниками с автодополнением кода: StarCoder и Codeium. Для демонстрации работы AI‑плагинов используем запросы, которые часто возникают в нашей работе. Подробнее о них расскажем ниже.

Введение

В посте мы рассмотрим задачу генерации кода — LLMs for Code.

Большие языковые модели ( они же Large Language Models или LLMs) представляют собой сложные нейронные сети, обученные на огромных объемах данных и предназначенные для обработки естественного языка. В зависимости от цели LLM можно обучить на специализированном наборе данных и применить для решения различных задач, например, распознавание именованных сущностей (NER), синтез текста в речь (text2speech), анализ тональности текста (SA) и другие.

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

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

Мы же рассмотрим две нейронные сети: StarCoder и Codeium. Эти модели были выбраны не просто так, они интересны нам по нескольким причинам:

  • область решаемых задач:
    • ускорение: автозаполнение кода;
    • исследование: генерация кода по комментарию на естественном языке;
  • поддерживаемый язык: Python;
  • наличие расширения в IDE Visual Studio Code;
  • открытый доступ (Open Source), бесплатный (free price);
  • модели умеют работать с русским языком;
  • дата выхода:

Немного о моделях

Starcoder — это большая языковая модель с не менее чем 15,5 миллиардами параметров, разработанная в сотрудничестве компаний Hugging Face и ServiceNow. Модель демонстрирует свою способность автоматически исправлять код, генерировать его по текстовому запросу и переводить с одного языка программирования на другой.

StarCoder превосходит самые масштабные модели, такие как PaLM, LaMDA и LLaMA, и это при том, что она заметно компактнее. Она также опережает модель CodeGen-16B‑Mono и представителя OpenAI code‑cushman-001.

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

Codeium — это набор инструментов для автодополнения кода, разработанный командой Exafunction. Модель использует технологию генеративного искусственного интеллекта для прогнозирования и предложения следующих строк кода на основе контекста и синтаксиса.

Рассматриваемая нейросеть не уступает в производительности таким моделям, как Copilot, Tabnine, Replit Ghostwriter и другие. Согласно последнему опросу разработчиков StackOverflow Codeium и Copilot являются двумя наиболее востребованными инструментами для кодирования AI.

Наиболее значимым преимуществом Codeium является система поиска контекста — context aware everything. Данная система решает сразу несколько проблем: одновременный анализ естественного языка и языка программирования с помощью docstrings, фрагментация контекста в зависимости от вопроса и гибридный поиск редких сущностей. Подробнее с этими функциями можно ознакомиться на сайте продукта.

Переходим к практике

Все практические примеры были реализованы в среде разработки Visual Studio Code.

Были установлены расширения:

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

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

В следующих примерах мы рассмотрим некоторые типовые задачи, с которыми сталкивается аналитик:

  1. Часто данные выгружаются в неудобном для чтения виде (например, в формате json‑файлов) и для дальнейшего анализа необходимо привести информацию в табличный формат.
import json
path = "testjson.json"
# прочти json-файл и выведи все ключи первого уровня, путь до файла указан в path
----- код от Starcoder -----

with open(path, "r", encoding="utf-8") as f:
    data = json.load(f)
    for key in data.keys():
        print(key)
----- код от Codeium -----

with open(path, "r", encoding="utf-8") as f:
    data = json.load(f)
    for key in data.keys():
        print(key)  

Обе нейросети сгенерировали одинаковый рабочий скрипт.

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

import pandas as pd
df = pd.read_csv("bank.csv", sep = ";")
# найди количество клиентов, которым меньше 40 и которые замужем
----- код от Starcoder -----

df_men = df[df["age"] < 40]
df_men_married = df_men[df_men["marital"] == "married"]
print(df_men_married.shape[0])
----- код от Codeium -----

print(df[(df["age"] < 40) & (df["marital"] == "married")])

Стоит отметить, что csv‑файл был импортирован в папку со скриптом, но название колонок нигде не упоминалось ранее. Сначала нам показалось, что AI‑плагины прочли названия столбцов, сопоставили с запросом и вывели необходимый код, но в ходе дальнейших запросов было замечено, что это была просто удача перевода и вероятнее всего нейросеть не «читает» соседние файлы в директории.

3. Иногда для быстрой проверки своей гипотезы аналитику необходимо получить заданный фрагмент текста, например, дату, номер договора или email. Это можно осуществить, используя регулярные выражения.

import re
test_string = '''
03.08.1882 — Конгресс США запретил въезд в страну лунатикам, преступникам и сумасшедшим, а также на 10 лет запретил иммиграцию китайцев.
'''
# найди дату в строчке test_string
----- код от Starcoder -----

# результат запиши в переменную date
# выведи на экран
date = re.findall(r'\d{2}\.\d{2}\.\d{4}', test_string)
print(date) 
----- код от Codeium -----

date = re.findall(r'\d{2}\.\d{2}\.\d{4}', test_string)

Обе нейросети справились с поставленной задачей. Стоит отметить, что Starcoder сам продолжил написание приведенных комментариев.

4. Некоторые аналитики работают с отзывами клиентов или комментариями сотрудников, и для ускорения проводимого анализа необходимы навыки в обработке естественного языка (NLP). Одним из этапов в этой работе является предварительная обработка текста, которая заключается в очистке текста от пунктуации, удалении стоп‑слов, токенизации, лемматизации.

text = """
В августе небо еще пронзительно синее, но уже чуть прозрачное, далекое – ему будто нет дела до того, что происходит внизу, и лишь смотрят вдаль далекие белые облака.
"""
# удали пунктуацию в строке text
----- код от Starcoder -----

text = re.sub(r'[^\w\s]','',text)
----- код от Codeium -----

text = text.replace(".", "")
text = text.replace(",", "")
text = text.replace(";", "")
text = text.replace(":", "")
text = text.replace("!", "")
text = text.replace("?", "")
text = text.replace("(", "")
text = text.replace(")", "")
text = text.replace("[", "")
text = text.replace("]", "")
text = text.replace("{", "")
text = text.replace("}", "")
text = text.replace('"', "")
text = text.replace("'", "")
print(text)

Как можно заметить, Starcoder справился с задачей. В свою очередь Codeium выбрал путь последовательной замены только некоторых знаков препинания, например, в нашем примере тире «‑» останется в предложении.

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

from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
df_X = pd.read_csv('toxic_comments_X.csv',  sep = ";")
df_y = pd.read_csv('toxic_comments_y.csv',  sep = ";")
# раздели данные на тренировочную и обучающую выборки и обучи модель логистической регрессии, рассчитав точность (accuracy)
----- код от Starcoder -----

X_train, X_test, y_train, y_test = train_test_split(df_X, df_y, test_size=0.2, random_state=42)
clf = LogisticRegression(random_state=0).fit(X_train, y_train)
print(clf.score(X_test, y_test))
----- код от Codeium -----

X_train, X_test, y_train, y_test = train_test_split(df_X, df_y, test_size=0.3)
model = LogisticRegression()
model.fit(X_train, y_train)
print(model.score(X_test, y_test))

Модели успешно разработали базовую модель машинного обучения.

6. Часто аналитикам необходимо импортировать информацию из базы данных. Рассмотрим пример подключения к СУБД, встроенной в Python.

import sqlite3
# создай подключение к базе данных my-test.db
----- код от Starcoder -----

conn = sqlite3.connect('my-test.db')
# создай курсор
cursor = conn.cursor()
----- код от Codeium -----

con = sqlite3.connect("my-test.db")

Обе нейросети справились с поставленной задачей.

7. Подробнее остановимся на базах данных. Для качественной и эффективной аналитики требуется выполнять SQL‑запросы. Поэкспериментируем и дадим нейронным сетям запросы с разным типом сложности:

Лёгкий уровень:

import sqlite3
conn = sqlite3.connect('city.db')
cursor = conn.cursor()
# Таблица city состоит из полей id, name, countrycode, district, population
# Напишите запрос, который посчитает численность населения (population) всех городов в таблице CITY, где District — 'Калифорния'
----- код от Starcoder -----

cursor.execute("SELECT population FROM city WHERE district = 'Калифорния'")
print(cursor.fetchall())
conn.close()
----- код от Codeium -----

cursor.execute("SELECT population FROM city WHERE district = 'Калифорния'")
print(cursor.fetchall())
conn.close()

Сгенерированные запросы верны.

Средний уровень:

import sqlite3
conn = sqlite3.connect('banks.db')
cursor = conn.cursor()
# База данных состоит из таблиц: clients (id_client, first_name, last_name), bills (id_bill, id_client, data_begin, data_close, code_bills), operations (id_bill, id_oper, type_oper, sum_oper)
# Напиши запрос для вывода всех id_client из таблицы clients, у которых количество операций больше трёх по типу 'снятие' (type_oper) из таблицы operations
----- код от Starcoder -----

cursor.execute("SELECT id_client FROM clients WHERE id_client IN (SELECT id_client FROM bills WHERE id_bill IN (SELECT id_bill FROM operations WHERE type_oper = 'снятие' GROUP BY id_bill HAVING COUNT(id_bill) > 3))")
print(cursor.fetchall())
conn.commit()
conn.close()
----- код от Codeium -----

cursor.execute("SELECT id_client FROM clients WHERE id_client IN (SELECT id_client FROM operations WHERE type_oper = 'снятие' GROUP BY id_client HAVING COUNT(id_oper) > 3)")
print(cursor.fetchall())
conn.close()

Codeium не справился с поставленной задачей. В свою очередь Starcoder сгенерировал вполне рабочий код.

8. Одной из задач аналитика является визуализация представленных данных.

Простой пример:

import matplotlib.pyplot as plt
import pandas as pd
df = pd.read_csv("client")
# построй круговую диаграмму по полю 'gender' в df
----- код от Starcoder -----

df.plot.pie(y='gender', figsize=(5, 5))
plt.show()
----- код от Codeium -----

plt.pie(df["gender"].value_counts())
plt.show()

Обе нейросети справились с задачей.

Немного усложним.

import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats
Y = [18.47, 14.87, 21.51, 9.07, 16.02, 11.12, 23.45, 6.45, 14.21, 8.18, 14.5, 3.86,
     10.14, 9.99, 14.47, 0.65, 8.97, 2.47, 12.58, 3.12, 6.81, 0.43, 4.65, 5.91]
t = np.linspace(0, 1, len(Y))
# посчитай коэффициенты и построй график линейного тренда 
----- код от Starcoder -----

a, b = np.polyfit(t, Y, 1)
plt.plot(t, Y, 'o', label='Original data', markersize=10)
plt.plot(t, a * t + b, 'r', label='Linear trend')
plt.legend()
plt.show()
----- ещё код от Starcoder -----

# с помощью функции linregress
a, b, r, p, err = stats.linregress(t, Y)
print(a, b, r, p, err)
plt.plot(t, Y, 'o', label    = 'Y')
plt.plot(t, a*t + b, 'r', label = 'a*t + b')
plt.legend()
plt.show()
----- код от Codeium -----

slope, intercept, r, p, std_err = stats.linregress(t, Y)
print(slope, intercept, r, p, std_err)
plt.plot(t, slope * t + intercept)
plt.scatter(t, Y)
plt.show()

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

Codeium выдал необходимый код сразу же.

Если сравнить данные решения по визуальной составляющей, то Starcoder справился лучше: модель добавила легенду и цвет.

9. В связи с быстрым увеличением объема информации были разработаны новые технологии для обработки и анализа этих данных. Многие крупные компании применяют фреймворк Apache Spark для выполнения распределенной обработки, и мы не стали исключением – мои коллеги ежедневно используют PySpark в своей работе.

import pyspark
from pyspark.sql import SparkSession
# создай spark-сессию
----- код от Starcoder -----

spark = SparkSession.builder.appName('task1').getOrCreate()
----- код от Codeium -----

spark = SparkSession.builder \
  .master("local[*]") \
  .appName("SparkByExamples.com") \
  .getOrCreate()

Codeium и Starcoder сгенерировали корректный код: наша spark‑сессия запустила.

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

import pyspark
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('Deposit').config(conf=conf).enableHiveSupport().getOrCreate()
path = './deposit.parquet'
# прочти данные из parquet-файла, путь указан в переменной path
# таблица состоит из bill_id, date_open, id_operations, type_operation
# выведи количество операций для типа (type_operation) - 'пополнение' для каждого счета (bill_id), открытого до 01.01.2023
----- код от Starcoder -----

df = spark.read.format("parquet").load(path)
def deposit():
    df_dep = df[
        ((df['date_open'] < "2023-01-01") &
         (df["type_operation"] == "Пополнение"))] \
           .groupBy(['bill_id'])\
               .agg({'id_operations': 'count'})
    return df_dep
----- код от Codeium -----

df = spark.read.parquet(path)
df.createOrReplaceTempView('deposit')
spark.sql('select bill_id, type_operation, count(*) from deposit where date_open <= "2023-01-01" and type_operation = "пополнение" group by bill_id, type_operation').show()

Как мы можем видеть, нейронные сети не всегда генерируют правильный и оптимизированный код, поэтому до роли аналитика им ещё далеко.

Также стоит отметить, что авторы все ещё ставят под вопрос как наличие, так и отсутствие возможности у нейронных сетей использовать соседние файлы. В ходе написания запросов возникали моменты, когда одна модель какое‑то время не могла сгенерировать код, а после выдавала идентичный тому, который сгенерировала другая модель. Но как мы уже говорили, все скрипты выполнялись в отдельных проектах, которые лежали в отдельных папках.

Краткие итоги

Подведём итоги в сравнительной таблице.

 StarcoderCodeium
Наличие веб-версииДа Подробнее: https://huggingface.co/spaces/bigcode/bigcode-playgroundДа Подробнее: https://codeium.com/playground
Наличие метода заполнения посередине (Fill in the Middle, FIM),Да Подробнее: https://huggingface.co/bigcode/starcoderДа Подробнее: https://codeium.com/blog/why-code-completion-needs-fill-in-the-middle
Возможность доработки модели, настройка параметровДа Подробнее: https://github.com/bigcode-project/starcoder#fine-tuningДа, но только в Codeium for Enterprises (данная версия платная) Подробнее: https://codeium.com/blog/finetuning-with-codeium-for-enterprises
Максимальное количество выводимых токенов81922048
Количество поддерживаемых языков программирования80+ Python, C++, Java, Kotlin, PHP, Ruby, TypeScript и другие70+ C, C++, C#, Go, Java, JavaScript, , Python, R, Ruby, Scala и другие
Среда интеграцииVSCodeVSCode, JetBrains, Jupyter / Colab / Deepnote / Databricks Notebooks, Vim / Neovim, Emacs, VSCode Web IDEs (ex. Gitpod), Chrome Extension

Оценка моделей.

РазделStarcoderCodeium
Get keys of json-file++
Filter Pandas DataFrame++
Use Regular Expressions++
Remove punctuation+
ML++
Connect to a db++
SQL queries+
Visualization+-+
Create a Spark Session++
Filter Spark DataFrame++
Score9.5/109/10

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

Вывод

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

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