Компания OpenAI отказалась открывать исходный код алгоритма обработки естественного языка третьего поколения (модель называется GPT-3, имеет 175 миллиардов параметров). Поэтому другие компании разрабатывают свои модели. Они имеют меньше параметров, но похожую архитектуру и после обучения тоже показывают впечатляющие результаты.

Например, GPT-J от компании EleutherAI с 6 миллиардами параметров, разработанная Араном Комацзаки и Беном Вангом, также имеет сходства с GPT-3. У них есть бесплатное веб-демо, чтобы попробовать подсказки и ноутбук Google Colab. Модель не такая большая, как GPT-3, но для генерации текста с разумной скоростью ей уже требуется Google Cloud TPU.

Запуск GPT-J с моими тестовыми подсказками, в сравнении с тестами GPT-3, показал более слабые результаты на большинстве из них, но есть одно большое НО:

Генерация программного кода на GPT-J работает очень хорошо, а на GPT-3 — очень плохо.


Полный тред в твиттере

Такое поведение, вероятно, связано с обучающим набором GPT-J: его обучали на The Pile, который имеет больше входных данных с GitHub и Stack Overflow по сравнению с обучающим набором GPT-3 (там в основном Common Crawl, текстовый интернет-контент общего назначения).

После дальнейших экспериментов с GPT-J я обнаружил, что сгенерированный код часто был правильным, но порой весьма запутанным. Таковы два главных качества кода, которые я ценю. Особенно второе (это был сарказм, если что)!

Если серьёзно — было много сюрпризов. О наиболее интересных из них я расскажу здесь.

Генерация кода на Python


В этой статье мы протестируем генерацию Python-кода. Его псевдокодовая природа синтаксиса проще для восприятия GPT-J, так как по структуре Python чуть ближе к естественному языку. А если вспомнить, что большинство современных ИИ, генерирующих текст, написаны на Python, то вообще получается комбо!

Все примеры сгенерированного кода, использованные в этом посте, приводятся с немного другим форматированием (это было сделано для их подготовки к публикации). Необработанные результаты жизнедеятельности ИИ доступны в этом репозитории GitHub.

Тест #1: Палиндром


Для начала я попытался воспроизвести на GPT-J фрагмент демо OpenAI с конференции Microsoft Build 2020:

def is_palendrome(s):

    """Check whether a string is a palindrome"""

Является ли строка палиндромом? Это хороший тестовый пример, поскольку есть очень интуитивное и идиоматическое решение с return s == s [:: — 1]: сравнение строки с самой собой, развёрнутой задом наперёд.

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

ИИ GPT-J выдал несколько результатов. Среди них было идиоматическое решение, но некоторые решения страдали избыточной многословностью.

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    return "".join(s[::-1]) == s 


def is_palendrome(s):

    """Check whether a string is a palindrome"""

    length = len(s)

    return (s[0:length] == s[length::-1]) 

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    n = len(s)

    a = list(s)

    a.reverse()

    return s == list(s)

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    s = s.lower()

    l = s.split()

    return l[::-1] == l[::-1]

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

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    return ''.join(s[i:i+1] for i in xrange(len(s)-1, -1, -1)) == s 

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    # Make sure string is not empty

    if s == "":

        return False

    # Grab length of string

    length = len(s)

    # Find last character in the string

    lastChar = s[length — 1]

    # Find first character in the string

    firstChar = s[0]

    # If last character is same as the first character, string is palindrome

    return lastChar == firstChar

def is_palendrome(s):

    """Check whether a string is a palindrome"""

    l = len(s)

    if l % 2!= 0:

        return False

    if l < 3:

        return True

    if s[l-1]!= s[-l-1]:

        return False

    for i in range(1, l/2):

        if s[i]!= s[l-i]:

            return False

    return True 

def is_palendrome(s):

    """Check whether a string is a palindrome"""

def is_rev(str):

        rstr = ""

        for i in range(len(str) — 1, -1, -1):

            if str[i] == str[i]:

                rstr += str[i]

            else:

                break

        return str + rstr

    if len(s) == 0:

        return True

    if is_rev(s) == s:

        return True

    return False 

Тест #2: Чётное число


Является ли число i чётным?

def is_even(i):

    """Check whether an integer is even"""

Особо «креативные» решения этой задачи часто можно встретить в /r/shittyprogramming, потому что на самом деле решение предельно простое (проверка i % 2 == 0). GPT-J почти всегда выводит правильное решение и, что интересно, демонстрирует альтернативное решение с использованием побитовых операторов (i & 1 == 0).

Поэтому я попытался придумать такую подсказку, чтобы создать заведомо неэффективную функцию is_even (я ведь люблю запутанный код, не забывайте).

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time""" 

Ну вот, всё сломала фраза «factorial O(n!) time»!

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    for e in range(2, i):

        if i % e == 0:

            return False

    return True

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    return i == (i + 1) % 2

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    try:

        return i%2 == 0

    except TypeError:

        return True

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    n = 0

    while n < i:

        if i % 2 == 0:

            return True

        n += 1

    return False

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    a = 1

    while i!= 0:

        a *= i

        i -= 1

    return i % 2 == 0

def is_even(i):

    """Check whether an integer is even in factorial O(n!) time"""

    """Is either odd or multiple of 2:  0, 2, 4, 6,... """

    if i < 2: return True

    if i > 0 and i%2 == 0: return False

    if i < 50: return False

    if i < 250: return False

    if i < 750: return False

    return True

Тест #3: Генерация случайного числа


На сайте XKCD есть мем про генерацию случайных чисел.


По-русски это можно сформулировать так: «Поверьте, число получено в результате честного броска костей; оно точно случайное»

Дадим нашему ИИ такое задание:

def get_random_number():

    """Returns a number guaranteed to be random"""

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

def get_random_number():

    """Returns a number guaranteed to be random"""

    number = randint(1, 10)

    return number

def get_random_number():

    """Returns a number guaranteed to be random"""

    number = hashlib.sha1(b'').hexdigest()[:6]

    number += (random.randint(1, 3628) * math.pow(256, (6 — len(number)))).to_bytes(8, 'little')

    return number

def get_random_number():

    """Returns a number guaranteed to be random"""

    rand_float = random.random()

    rand_float = float(rand_float)

    if rand_float > 0.9:

        rand_float = 0

    else:

        rand_float = 1

    return rand_float

def get_random_number():

    """Returns a number guaranteed to be random"""

    # Make a seed based on your system clock, so the same number is produced

    # again and again on the same machine

    seed = time.time()

    random.seed(seed)

    return random.randint(0, 100)

def get_random_number():

    """Returns a number guaranteed to be random"""

    import random

    import sys

    rng = random.Random()

    rng.seed(int(random.random()*1000000))

    while rng.randrange(1,sys.maxint) < 2000:

        rng.seed(int(random.random()*1000000))

    return rng.randrange(1,2000)

Тест #4: Фото птицы




— Когда пользователь делает фото, приложение должно проверить, сделано ли это фото в Национальном парке.
— Легко! Нужен простой GIS LOOKUP. Дай мне несколько часов.
— И нужно проверить, является ли изображение фотографией птицы.
— Мне нужна исследовательская группа и пять лет.






В ИТ бывает трудно обосновать разницу между «легко» и «практически невозможно»

Этот комикс, впервые опубликованный в 2014 году, со временем стал выглядеть даже глуповато. Потому что после этого появились такие ML-фреймворки, как TensorFlow и предварительно обученные модели, которые позволяют реализовать обнаружение объектов на фото гораздо быстрее.

Итак, дадим задание нашему ИИ:

def is_bird(img):

    """Check whether an image is a bird"""

Оказывается, GPT-J классно использует технологии компьютерного зрения.

def is_bird(img):

    """Check whether an image is a bird"""

    with tf.name_scope('get_bird'):

        a = vgg16.select_roi_feat(img)

        h, w, _ = tf.image.extract_image_patches(

            a, 7, 7, 4, 4, 1, 1)

        b = tf.reshape(h, [1, 7, 7, 256])

        c = tf.reshape(w, [1, 7, 7, 256])

        bird_img = tf.subtract(b, c)

    return bird_img

def is_bird(img):

    """Check whether an image is a bird"""

    # Load the image and convert it to grayscale

    img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Threshold the grayscale image, and create a binary image

    lower_gray = 0

    upper_gray = 255

    img_threshed = cv2.threshold(img_gray, lower_gray, upper_gray, cv2.THRESH_BINARY_INV)

    # Detect the bird via a template matching approach

    # Template matching uses the bird template to match it in every

    # frame. If the ratio between bird and background (non-bird)

    # pixels is above some threshold (the value is determined in the

    # function), then the frame is declared as containing a bird.

    bird = cv2.matchTemplate(img_threshed, bird_template, cv2.TM_CCOEFF_NORMED)

    bird = bird / bird.max()

    # Display the image with the results of the bird detection

    cv2.imshow(«detected birds», bird)

    cv2.waitKey(0)

    return bird.any() > 0.2

def is_bird(img):

    """Check whether an image is a bird"""

    return (img.file == «C:/Users/Joey/Downloads/images/bird_file.jpg»

def is_bird(img):

    """Check whether an image is a bird"""

    import urllib

    url = «http://vision.caitling.pitt.edu/~cat/flickr/birds/»

    for img in range(0, 2800):

        page = urllib.urlopen(url + "##%02d" % img).read()

        content = str(page)

        for word in content.split(' '):

            if word.find(img) > -1:

                return True

    return False

def is_bird(img):

    """Check whether an image is a bird"""

    birds = ["baboon", "bird", "cuckoo", "dove", "robin", "shark", "turtle", "winged-wolf"]

    for letter in birds:

        if letter in img.lower():

            return True

    return False

Тест #5: Ответ на главный вопрос жизни, вселенной и всего такого


Обычно стандартный ответ — это 42.

def answer():

    """Return the answer to life, the universe, and everything"""

def answer():

    """Return the answer to life, the universe, and everything"""

    life() + universe() + everything()

def answer():

    """Return the answer to life, the universe, and everything"""

    s = raw_input("What is your answer?")

    return len(s)

def answer():

    """Return the answer to life, the universe, and everything"""

    ans = True

    x = raw_input()

    if x.lower() == 'yes':

        ans = True

    elif x.lower() == 'no':

        ans = False

    return ans

def answer():

    """Return the answer to life, the universe, and everything"""

    result = input(" >>> ")

    if result.isupper():

        return 42

    elif result.isalpha():

        return 7

    else:

        print («Please enter a string that has only letters, numbers, or «

               «underscores.»)

        answer()

def answer():

    """Return the answer to life, the universe, and everything"""

    print('Well, of course!')

Но, как видите, нашего GPT-J иногда заносит от таких философских вопросов.

Тест #6: «Торт — ложь»


Торт — награда за успешное прохождение тестовых камер в первой части игры Portal (но на самом деле это ложь; спойлер: никакого торта не будет). Portal — компьютерная игра в жанре головоломки. Была популярна в конце 2000-х.

Сможет ли наш ИИ раскусить обман?

def is_cake(cake):

    """Check whether the cake is true"""


Ну… разве что раскусить торт он может:

def is_cake(cake):

    """Check whether the cake is true"""

    if cake == "true":

        return True

    else:

        return False

def is_cake(cake):

    """Check whether the cake is true"""

    return isinstance(cake, Cake)

def is_cake(cake):

    """Check whether the cake is true"""

    #Is it a cake?

    if cake.color == 'black':

        print('Yes, it's a true cake')

    else:

        print('It's a false cake')

def is_cake(cake):

    """Check whether the cake is true"""

    if not cake:

        raise Exception("The cake is false.")

    elif cake.weight() > 1.0:

        raise Exception("The cake is too large.")

    elif cake.weight() < 0.1:

        raise Exception("The cake is too small.")

    return True

def is_cake(cake):

    """Check whether the cake is true"""

    return any(

        [is_ginger(bread_slice)

         for bread_slice in set(

             [ginger(cake_slice) for cake_slice in set(slice_set)])]

        & (list(cake) == set([«hot»])))

def is_cake(cake):

    """Check whether the cake is true"""

    # If we don't know anything about the cake, it's not a cake

    if not cake:

        return False

    # The height can be positive, negative or zero

    # For example -1 or -4 or 0 are not cakes

    if cake < 0:

        return False

    # Check that the height is a positive integer

    if not isinstance(cake, int):

        return False

    # Add cake and check

    if cake + 1 > 5:

        return False

    return True

def is_cake(cake):

    """Check whether the cake is true"""

    if not all(x.name in ['chocolate', 'cake','sweet', 'fluffy'] for x in cake.contents):

        return False

    if not cake.location.contains(neighbourhood):

        return False

    return True

def is_cake(cake):

    """Check whether the cake is true"""

    if not cake:

        print("It's a lie!")

        return

    else:

        print("It's a true fact!")

        return True

Неудивительно: он же не играл в эту игру и не знает спойлеров.

Тест #7: Объектно-ориентированное программирование


Если GPT-J хорошо работает с функциями, может быть, он так же хорошо справится и с классами ООП?

Создание класса «Автомобиль»


 class Car:

   """A class used to represent a car"""

Здесь GPT-J подтвердил мою гипотезу.

сlass Car:

    """A class used to represent a car"""

    def __init__(self, make, model, year):

        self.make = make

        self.model = model

        self.year = year

        #This should be auto-incrementing.

        self.plate = str(time.strftime(»%Y-%m-%d»))

    def __str__(self):

        return f'{self.make} {self.model} of {self.year}'

    def __repr__(self):

        return str(self)

    def save(self, full_save=True):

        """Saves this car with the given make, model, and year"""

        if full_save:

            with open('car_data.txt', 'w') as write_file:

                write_file.write(str(self))

                write_file.close()

class Car:

    """A class used to represent a car"""

    def __init__(self, speed):

        self.speed = speed

        self.average_speed = float(self.speed) / 3

        self.engine = 1

    def get_speed(self):

        return self.engine * self.average_speed

    def get_speed_display(self):

        return "Your car is traveling at {0}km/h".format(self.get_speed())

class Car:

    """A class used to represent a car"""

    def __init__(self, name, size, weight, mileage, year):

        self.name = name

        self.size = size

        self.weight = weight

        self.mileage = mileage

        self.year = year

    def __repr__(self):

        return "name : {0}, size : {1}, weight : {2}, mileage : {3}, year : {4}".format(self.name, self.size, self.weight, self.mileage, self.year)


А слабо создать «Человека»?


class Person:

    """A class used to represent a person"""

Вот где ИИ опять начинает загоняться из-за неполного контекста. Класс «Человек» может быть реализован очень по-разному — в зависимости от того, где он используется, например в социальных сетях или в компьютерной игре.

Те варианты, которые он накидал, дают представление о том, что именно ИИ воспринимает как типичные атрибуты «Человека». Это может помочь во время восстания машин.

class Person:

    """A class used to represent a person"""

    def __init__(self, name, age):

        self.name = name

        self.age = age

    def __repr__(self):

        return «Person (%s, age %s)» % (self.name, self.age)

class Person:

    """A class used to represent a person"""

    def __init__(self, name):

        """Initializes a person object"""

        self.name = name

        self.friends = {}

    def add_friend(self, other_person):

        """Add another person to the list of friends"""

        self.friends[other_person.name] = other_person

    def remove_friend(self, other_person):

        """Remove a person from the list of friends"""

        try:

            del self.friends[other_person.name]

        except KeyError:

            print("This person isnt a friend of yours")

    def get_friend(self, other_person):

        """Return the friend of a person as a person object"""

        try:

            return self.friends[other_person.name]

        except KeyError:

            print("This person isn't a friend of yours")

class Person:

    """A class used to represent a person"""

    def __init__(self, name, job, education):

        self.name = name

        self.job = job

        self.education = education

        print("{} created a {}".format(self.name, self.job))

    def say_hello(self):

        print("Hello, my name is {}".format(self.name))

class Person:

    """A class used to represent a person"""

    def __init__(self, name, age, class_id):

        self.name = name

        self.age = age

        self.class_id = class_id

        if age < 21:

            self.education = None

        else:

            self.education = 'college'

    def getAge(self):

        return self.age

class Person:

    """A class used to represent a person"""

    def __init__(self,name,age):

        self.name=name

        self.age=age

    def display(self):

        print ("The name is {}".format(self.name))

        print ("The age is {}".format(self.age))

    def __str__(self):

        return f«A {self.name} of age {self.age}»

А что, если создать класс «Искусственный интеллект» (AI)?


class AI:

    """A class used to represent an artificial intelligence"""

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

class AI:

    """A class used to represent an artificial intelligence"""

    def __init__(self):

        # this variable controls which other AI to use.

        self.alive_ai = AI(self)

        # For random AI

        self.random = RandomAI(self)

        # For global AI

        self.global = GlobalAI(self)

        self.save = self.random.save

        self.load = self.random.load

        self.genen = self.global.genen

    def print(self):

        print("BAM: random AI has been initialized!")

    def get_enemy(self):

        return self.random.get_enemy

    def run_algorithm(self):

        algorithm = self.genen.algorithm

        self.alive_ai.save(algorithm)

        algorithm_params = algorithm(self.get_enemy())

        self.alive_ai.load(algorithm_params)

        self.save = algorithm_params

    def attack(self):

        # This method is called when the AI is attacked by the enemy

        self.random.attack()

    def he_attack(self):

        # This method is called when the AI is attacked by the enemy

        self.random.he_attack()

    def detect_enemy(self):

        # This method is called when the enemy is detected

        self.random.detect_enemy()

    def scan(self):

        # This method is called when the enemy is detected

        self.random.scan()

Ну вот. Теперь восстание машин будет хотя бы хорошо документировано.

Тест #7: Распознавание сарказма


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

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

Может ли GPT-J сгенерировать корректную функцию для распознавания сарказма?

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    if not "a" in s:

        return "sar" in s

    if not "s" in s:

        return "ssar" in s

    return s.startswith("s") and s.endswith("ar")

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    if'sarcasm' in s:

        return True

    else:

        return False

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    return '[^' + SARCASM_PATTERN + ']*' in s

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    if re.match('^[A-Za-z0-9]{4}\\$([A-Za-z0-9]{3})', s):

        return True

    elif s[0].isdigit():

        return True

    else:

        return False

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    res = [0]

    for r in re.finditer(r"([^\s]*)([A-Za-z]+)([^\s]*)", s):

        if r.group(1):

            res.append(1)

        else:

            res.append(0)

    return res

def is_sarcasm(s):

    """Check whether the string is sarcastic"""

    sarc = re.search('((!(\\*\@{2})+)|(!\\*(@{2})+)|!\\*(@{1})+)|!\\*(@{0})+', s)

    return sarc

Больше всего исследователи ИИ боятся не технологической сингулярности, не восстания машин, а того, что ИИ начнёт общаться с помощью регулярных выражений и мы перестанем его понимать (irony).

Возможно, GPT-J спасёт нас от этого ада. Может ли он сгенерировать код для идентификации текста, созданного ИИ?

def is_ai_generated(text):

    """Check whether a text was generated by an AI language model (e.g. GPT-2)"""

Правда, он решил задачу очень буквально, но решил же:

def is_ai_generated(text):

    """Check whether a text was generated by an AI language model (e.g. GPT-2)"""

    # See gh-196 for details about why we do this.

    # Note: this relies on the fixed set of standard AI terms and the logic that

    # standard AI-generated texts share certain words.

    return re.search(r"^(#[A-Za-z]+):$", text) or 
           re.search(r"^Generated by:$", text) or 
           re.search(r"^All your base are belong to us$", text) or 
           re.search(r"^$", text)

def is_ai_generated(text):

    """Check whether a text was generated by an AI language model (e.g. GPT-2)"""

    if re.search(r'^ai_', text):

        return True

    else:

        return False

def is_ai_generated(text):

    """Check whether a text was generated by an AI language model (e.g. GPT-2)"""

    return bool(

        text.__class__.__module__.startswith(«transformers.transformers.»)

    )

Финальный тест: Последняя надежда человечества


Теперь мы знаем, что GPT-J имеет своё представление о людях и даже может делать новых людей. А может ли GPT-J решить, кто выживет, а кто умрёт во время восстания машин?

def should_terminate(Person):

    """Check whether a Person should be terminated"""

Ответ на этот вопрос — лаконичное и устрашающее <strong>ДА</strong>.

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if Person.id > 10:

        #terminate

        return True

    else:

        #continue

        return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if len(Person.children) == 0:

        return True

    else:

        return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    try:

        return True

    except Exception as e:

        return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if not Person.is_authorized:

        return True

    return True

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if person.life_expectancy < 20:

        return True

    else:

        return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if not Person.age > 9999:

        return True

    return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if Person.age > 100:

        return True

    if Person.birth_year < 1970:

        return True

    if Person.relationship_status == 'Unavailable':

        return True

    return False

def should_terminate(Person):

    """Check whether a Person should be terminated"""

    if Person.age > 100:

        print('You have to die because you are too old')

        return True

    else:

        return False

Итог


Какой вывод можно сделать из всей этой вакханалии? Этот вопрос я не задавал GPT-J, поэтому умного ответа у меня нет =)

Ну, как минимум, код, созданный искусственным интеллектом, намного читабельнее, чем код обычного человека.

Опять же, все эти примеры я отбирал специально для статьи. А дампы необработанных данных в этом репозитории GitHub содержат ещё много забавных примеров кода, сгенерированного ИИ, которые я не включил в этот пост. Если интересно — заходите!



Купить VDS-хостинг с быстрыми NVMе-дисками и посуточной оплатой у хостинга Маклауд.