Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Компания 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е-дисками и посуточной оплатой у хостинга Маклауд.