| |
Решатель ARC-задач Франсуа Шолле | |
| |
create
|
Решатель ARC-задач Франсуа Шолле |
mserg и Egg предложили тестировать AGI-прототипы на задачах, которые разработал Франсуа Шолле. В связи с этим я решил открыть специализированный топик, посвящённый практике создания прототипов AGI для решения данных задач.
Сами задачи в json-форматах и веб-интерфейс для их визуализации можно скачать здесь.
ARC - это авторская аббревиатура, расшифровывается как "The Abstraction and Reasoning Corpus", что можно перевести как "задачи на абстракцию и умозаключения". Возможно, автор хотел намеренно добиться аллюзии на английское слово ark - "ковчег", в ноевском варианте которого, как известно, каждой твари было по паре.
Рассмотрим пример задачи из файла 6150a2bd.json
В данном примере представлены две пары, два временны́х среза, показывающие, как задачная ситуация (сцена) выглядела до преобразования (слева), до срабатывания некоторого правила, и как она выглядит после преобразования (справа). Задача испытуемого (человека или AGI-прототипа) выявить правило, по которому произведено данное преобразование, и далее, применяя это правило для тестового изображения, получить результирующее изображение. В других json-файлах количество таких демонстрационных пар может быть иным.
Это тестовое изображение для данного примера. То есть правило, выявленное из анализа двух вышеприведённых пар изображений, нужно применить к данному изображению. И результат преобразования ввести в итоговое поле. Тестовых изображений может быть несколько.
Это пример итогового поля, в которое нужно ввести изображение, полученное после применения выявленного преобразования к тестовому изображению. В некоторых задачах размер итогового поля отличается от первоначального, поэтому определение и задание размера итогового поля становится отдельной задачей.
Достоинства данных задач:
- их много разных, разработчику схитрить не удастся, придётся делать действительно универсальный решатель;
- они предельно простые, весь алфавит задач состоит только из 10 цветов и квадратов, совмещённых в прямоугольники;
- в каждой задаче есть верный ответ, с которым можно свериться;
- все задачи имеют цифровой вид, удобный для "восприятия" компьютером.
Цель данного топика разработать прототип минимального AGI для решения всех 800 задач без ошибок, что будет свидетельствовать о достаточной универсальности и работоспособности решателя.
Приглашаются все желающие.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Во-первых, хочу предостеречь.
1. 800 правильных результатов получить не получится, так как, скорее всего, в наборе задач есть ошибки. Часть из них была исправлена - это видно по правкам в репозитории в гитхабе.
2. Нельзя менять местами пары изображений в задаче. Может показаться, что в задаче преобразуется матрица в матрицу. В действительности, преобразуется пара (матрица, "номер задачи в наборе") в матрицу.
Архив скриптов вместе с данными находится тут:
http://np-soft.ru/downloads/abstract2019.zip
Тут, скорее всего содержатся не исправленные примеры, которые выправили в гитхабе.
В архиве папки training, test и evaluation содержит наборы данных (900 штук: +100 за счет test).
Что в папках training_ и py/training - не помню. Возможно, одна папка содержит найденные ошибки, вторая - "необработанные" данные (см. ниже). В папке py находятся скрипты.
* view.py - просмотр файла с числами. Скрипт вызывается с именем файла-json-а. Иногда важно видеть цифры, а не цвета - скрипт может пригодиться.
* manual.py - содержит скрипты для "решения" задач. Каждая задача представлена в виде класса с именем Example_папка_файл. Например, класс Example_training_50846271 содержит решение задачи из папки training для файла 50846271.json. Вызов diffAll() покажет, количество различий, даваемых расчетом в классе и данными в библиотека. По умолчанию вызывается Example_training_50846271().diffAll(), который выводит [0, 0, 4, 0, '|', 0]. Это означает, что в примере 3 есть расхождения в 4-х клетках.
* tparser.py - библиотека парсинга.
Что это и зачем - в следующем сообщении.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
20:41 22.10.2022 |
|
20:41 22.10.2022 |
|
№5951 |
Egg:
Ну и нужно понимать, что задач здесь не 800, а много больше, поскольку любая пара - это задача на генерацию правила.
Это точно не так - одна функция на все пары в рамках одной задачи. И как я уже говорил, задач 900. Можете открыть архив и посчитать. :-) Но я настаивать не буду.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
20:46 22.10.2022 |
|
20:48 22.10.2022 |
|
№5952 |
Вместо предисловия: «Не льсти себе – подойди поближе» - надпись в туалете над писсуаром.
Математическая постановка звучит следующим образом: для каждой задачи из набора данных найти функции преобразования исходной матрицы в результирующую. Если иметь в виду, что эти функции могут иметь общие «фрагменты», то к этим функциям нужно добавить еще эту общую часть – библиотеку.
Критерий качества составляется из следующих частей.
1. Количество правильно решенных тестовых задач
2. Доли расхождений в отображении тестовых задач, если есть различия
3. Количество правильных преобразований исходной матрицы в результирующую для задачи
4. Доли для пункта 3.
5. Максимальная абстракция – в качестве оценки будем использовать количество «закорючек», используемых для описания функций и библиотеки. Т.е. нам нужно минимизировать количество закорючек.
Дополнительные ограничения
1. Потребуем, чтобы используемый «объем памяти» при решении не превышал «100 кб.»
2. Время всех вычислений для каждой из задач не превышал условные 100мс.
Если ограничения нарушаются, то считаем, что выходная матрица равна входной.
Далее нужно задать «сигнатуру», т.е. элементарные операции, из которых может создаваться функция. Здесь можно пойти задам наперед. Разрешим для использования базовые конструкции Python + работа с матрицами + структуры (классы, например). И скажем, что можно использовать математику, которая за этим стоит.
Вроде все. Создание системы, которая решит задачу подбора функции для каждой из задач и библиотек – это огромная и сложная задача. Поэтому, нужно подойти к решению проблемы по этапам.
Довольно быстро выяснилось, что нужно сначала пойти по ручному варианту создания функций и библиотеки. Что, собственно, и выложено в архиве. Там есть решение (решающая функция) почти всех задач, и хорошо бы этот этап завершить. Если кто-то найдет, чего там еще нет (это можно сделать скриптом), и дополнит пробелы в этой работе – буду очень благодарен. Этот этап, в некотором смысле, помогает формализовать решение задач. В принципе, с помощью AST (Abstract Syntax Tree) можно Python-ские скрипты превратить в «данные» для работы на более высоком уровне. В частности, можно проявить «сигнатуру» (наборы функций и структур Python, необходимых для решения этой проблемы). И т.д.
Как видно из скриптов, что решающие задачи функции имеют довольно большую длину даже без учета парсинга изображений. Поэтому перебор функций даже после парсинга практически невозможен. Поэтому нужны механизмы, которые создадут более эффективным механизмы сборки функции (алгоритма). Например, создание описания структуры изображений на основе характеристик теории информации. Тогда начальную проблему можно превратить в проблему «выравнивания» структур исходного и результирующего изображения. Другой пример, - найти разницу между исходным и результирующим изображением, и свести задачу к нивелированию разницы. Т.е. нужно опять просмотреть все задачи, и для каждой из них создать структуры / действия, повторяющие действия человека. Этот этап нас еще ближе приблизит нас к цели. И т.д.
Тут главная проблема – борьба с астрономическим количеством различных функций. Нам нужно найти механизмы, которые обходят это колоссальное количество. И, господа, прежде чем рассказывать, как нужно все делать правильно, покажите что Вы сделали. Но вопросы могут быть любые.
|
| |
create
|
Решатель ARC-задач Франсуа Шолле |
mserg: Дополнительные ограничения
1. Потребуем, чтобы используемый «объем памяти» при решении не превышал «100 кб.»
2. Время всех вычислений для каждой из задач не превышал условные 100мс. Каков смысл этих ограничений? Если AGI будет использовать 100 МБ ОЗУ и думать 3 часа, но решит при этом все задачи, это будет засчитано за слив?
mserg: Довольно быстро выяснилось, что нужно сначала пойти по ручному варианту создания функций и библиотеки. Тут тоже не понятно. Вы лично, когда решаете эти задачи, в уме составляете какие-то математические функции? Или всё-таки Ваш естественный I работает как-то иначе. Ведь в этой задачке интересно не просто во что бы то ни стало решить конкретно эти 800 задач, а протестировать универсальный AGI-прототип, т.е. который будет думать как человек, а не как мешок с математическими функциями.
mserg: нужны механизмы, которые создадут более эффективным механизмы сборки функции (алгоритма) Это можно сказать "святой грааль" AGI. Тут с наскока не получится.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Берем задачу 6150a2bd, которая демонстрировалась выше. По номеру находим ее в файле manual.py. Там есть функция преобразования convert, которая превращает исходное изображение в целевое. Полностью она выглядит так:
class Example_training_6150a2bd(Example):
def convert(self, parser):
return np.rot90(parser.image, 2)
Т.е. искомая функция - поворот изображения на 180 градусов. Система перебора функций, конечно, найдет это решение. И еще какое-то количество решений находится перебором (точно не помню сколько, может 1%-2%). Сама идея отбора функций по минимальному количеству закорючек (при прочих равных) вполне себе работает.
Далее, если не ограничивать ресурсы при вычислении функций, то все быстро сдохнет. Например, в Python
9 ** 9 ** 9
зависает намертво, хотя закорючек всего 5 (операции и цифры принимает за 1 закорючку). Да и интеллект, вообще, связан с использованием минимального количества ресурсов. Правильно было бы засунуть потребление ресурсов в критерий, но в этой задаче это лишнее усложнение. Поэтому указаны простые ограничения от балды.
Замороченная оценка качества, точнее, степень совпадения "предсказания" с результатом, тоже весьма полезна, т.к. мы видим, что есть ошибки в данных.
Итак, "мешок функций" все же не имеет признаков неработоспособности - есть подтвержденный критерий о том, чем одна функция при решении задачи лучше другой. Задачу (для всего набора данных) поставить мы можем, не можем решить эффективно. Конечно, может выясниться, это не совсем так. Да, допускаю. Мне казалось что это очевидно и тут нечего обсуждать.
----
Зачем вообще заниматься ручным созданием решений, когда нужно создать универсальный интеллект, а не решить конкретные задачи? Ответ в хохме про писсуар. Конечно, хочется сделать нечто, а потом натравить на задачи и получить результат. К сожалению, скорее всего, это закончится ничем.
Можно, конечно, заявлять, что работа с задачами приведет к обучению "программиста", а не самой системы ИИ. В какой-то мере так оно и есть, а в какой-то - нет.
Один мой знакомый, которому я показывал перебор функций, воскликнул - да это же дискретная оптимизация! Да, есть что-то похожее. Если покопать "промышленные" системы решения дискретных задач (CBC, например), то там есть и перебор (вершин полиэдра в симплекс-методе при решении релаксационной задачи), и еще перебор (дерево в методе ветвей и границ), есть сжатие границ, преобразования уравнений и уменьшение их количества, есть эвристики при поиске в дереве, есть эвристики для поиска допустимых решений, и т.д. и т.п.
Универсальный способ решения дискретных задач - это перебор. Для экспоненциального уменьшения вариантов в некоторых ситуациях могут быть применены полиномиальные (в смысле сложности) алгоритмы. Но применение этих полиномиальных алгоритмов связано с издержками, и поэтому их применение связано с другими алгоритмами более высокого уровня. Если на полную врубить все алгоритмы нижнего уровня, то все сдохнет. Поэтому алгоритм второго уровня аккуратно проверяет условия эффективности применения алгоритмов нижнего уровня, собирает статистику по эффективности их применения и делает коррекции на их основе, и т.д.
Так вот. Мы можем применить эти принципы для построения решателя для набора задач. Вопрос о том, обучается ли наш мозг при этом, или же мы получаем какие-то результаты, будет все время стоять. Ну, можно ничего не делать. А можно создавать решатель, и смотреть, как он устроен. Посмотреть, какие принципы себя зарекомендуют.
------
Ручное решение почти все задач вручную дало довольно много. Хотя можно утверждать обратное.
Во-первых, выяснен минимальный программно-математический аппарат для создания решателя.
Во-вторых, простота обработки изображений обманчива - борьбу с задачами нужно разделить на выделение информации из изображений и все остальное.
В-третьих, убедился, что мозг тоже занимается перебором (следил за собой, когда в уме решал задачи). Есть "градиентное" движение при решении (шаг по уменьшению различий матриц). Есть релаксация задачи и создание кусков алгоритма на этой основе (приводил пример с построением гистограмм).
В-четвертых, убедился, что все задачи нужно решать "одновременно". Иначе скачок сложности может оказаться непреодолимым.
|
| |
create
|
Решатель ARC-задач Франсуа Шолле |
06:57 23.10.2022 |
|
06:57 23.10.2022 |
|
№5959 |
Эти задачи как раз тот пример, когда только логики недостаточно. Тут нужно воображение, внутренний взор. Ведь мы воспринимаем эти картинки и их сочетание глазами. И видим различные узоры. То общие, то локальные. И именно система воображения дожна производить умственные эксперименты, по анализу которых разум сможет выискивать закономерности, которые в конце концов сольются в функции трансформации, являющиеся решаниями.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Egg
(9**9)**9 это 196627050475552913618075908526912116283103450944214766927315415537966391196809
А 9**(9**9) - это зависание
Целочисленная арифметика в Python написана на C. Поэтому смена языка в данном случае ничего не даст.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
11:27 23.10.2022 |
|
12:06 23.10.2022 |
|
№5964 |
Речь идет не о переборе, а о задании пространства, в котором нужно найти функцию с необходимыми нам свойствами. Поиск в пространстве действительно можно вести прямым перебором, но это не представляет практического интереса. Это не значит, что пространство функций задано неправильно или что критерий подбора функций неправильный. Наоборот, для простых задач подтверждается, что это годная постановка задачи. Не знаю, как еще объяснить...
Если мы посмотрим на классические алгебраические задачи, то там то же самое. Задаются переменные (пусть это будут ограниченные целочисленные), ограничения и критерий. Постановка задачи таким способом не означает, что есть в наличии эффективные алгоритмы ее решения. Гипотетически можно перебрать все варианты, но это на практике не реально. Разработано множество различных мат. пакетов, которые с разной степенью успешностью борются с такими задачами. Одни пакеты не могут решить одни задачи, другие пакеты - другие. Однако использование пакетов на практике приносит пользу, хотя и множество нерешаемых на данных момент задач велико.
Что касается задач ИИ, то ее постановка отличается от алгебраических задач только тем, что в качестве переменных могут быть функции. И все. В постановке задачи участвуют функции и их свойства. Получается своего рода функционально-алгебраическая система, в которой могут участвовать обычные переменные, переменные-функции, можно задавать функции-свойства функций переменных (например, потребление ресурсов), ограничения и можно задавать критерий.
UPD. На счет Python и разрядности...
Гарантировать, что какой-нибудь промежуточный результат не выкинет какой-нибудь фортель заранее при поиске функций не очень то реально. При "переборе" функция может случиться "любой". Ну вот промежуточная матрица может случиться большой и память кончится. Поэтому есть в постановке задачи ограничения по ресурсам. Ограничения должны отсекать зависания и зажоры памяти, а не я.
|
| |
create
|
Решатель ARC-задач Франсуа Шолле |
Некоторые предварительные соображения.
Привлекательность данной задачки в том, что каждый может на ней продемонстрировать эффективность тех идеологий, на которых он стоит - конкуренции, капитализма, гедонизма, дарвинизма, материализма, математики, комбинаторного множества элементов, воображения или чего ещё. Мне, конечно, ближе мои теории. Поэтому с точки зрения основополагающих идеологий солипсизма, психологии, креационизма, кооперации, местологии, нормологии и механизмологии я вижу этапы работы местного AGI следующим образом.
1. AGI, воспринимая пары обучающих изображений, получает и запоминает свой субъективный жизненный опыт - хронологические последовательности событий. Несмотря на то, что изображения статические, время здесь присутствует неявно в том, что оно обуславливает изменения, происходящие при переходе от первой картинки ко второй. Теоретически, пары обучающих изображений можно поменять местами, но тогда и решающие правила окажутся другими.
2. Из этого своего жизненного опыта AGI по некоторой универсальной процедуре должен выявить нормы, причинно-следственные отношения для данного конкретного json-файла. В виде, конечно, отношений субъективных понятий. Должно возникнуть понимание.
3. Ввиду того, что пар обучающих изображений в каждой задаче несколько, каждая следующая пара представляет собой полигон для испытаний знаний, полученных из первой пары. Если первая пара в каждой задаче даёт некоторые начальное понимание, то вторая и последующие пары должны это понимание подтвердить или опровергнуть (валидировать). Каждая последующая пара может и должна быть воспринята AGI критически, как нечто, что требует выявления отклонения он норм и исправления этого. Во всяком случае, мой И работает на этих задачах именно так. На этом этапе AGI может исправлять только свои знания. Мир даёт представление о норме, о том как должно быть, а AGI должен этому научиться, перенять в виде своих представлений. Впрочем, mserg утверждает что в наборе есть ошибки. Именно на этом этапе они могут и должны быть выявлены. Ведь ошибка - это отклонение от некоторой нормальности.
4. Тестовая картинка + пустая итоговая картинка по сути представляют собой очередную и последнюю по хронологии сцену жизненного опыта, её восприятие и запоминание. Но, что ещё более важно, эта пара картинок может и должна быть воспринята AGI не просто критически, как нечто, что требует выявления отклонения он норм, но и активного исправления в данном случае мира, а не своих знаний. Ведь если предположить, что вдруг итоговая картинка окажется не пустой (не чёрной), а уже содержащей некоторое изображение и даже более того, содержащей правильное изображение, т.е. соответствующее нормам выявленным ранее, то предпринимать далее, очевидно, ничего не потребуется. Решённые задачи не решаются.
5. Если итоговая картинка содержит изъяны, если она не такая, какой должны быть, ненормальная (а это в 100% случаев будет именно так, потому что она чёрная), то AGI должен синтезировать механизм из своих эффекторов, который исправит ситуацию и приведёт её в норму. Т.е. решит задачу. Эффекторов у AGI в данной задаче совсем немного:
1. изменение размеров итогового поля;
2. изменение цвета произвольного квадратика на итоговом поле (в человеческом интерфейсе ещё есть возможность скопировать тестовое поле в итоговое и применить заливку цветом, но для железного AGI эту возможность можно убрать, ведь ему нет нужды экономить время и силы, пусть мучается и рисует по квадратикам).
Механизм - это по сути алгоритм действий. Ведь даже чтобы нарисовать простой "цветочек" вокруг квадратика, AGI должен решить, с какого квадратика ему начать и каким закончить.
Что меня несколько смущает. В наборе есть некоторые задачки, решение которых для человека является очевидным и простым потому, что он обладает большим жизненным опытом нашей обычной (а не цвето-квадратной) реальности. Предполагаю, что в некоторых случаях AGI либо не сможет решить такую задачку т.к. упрётся в комбинаторную стену, либо решит её совсем не так, как решает её человек. Тут же возникает предположение, что для совсем корректного моделирования работы интеллекта стоило бы сначала позволить AGI поиграть в игрушечном мире цветных квадратиков, поднабираться детского опыта, чтобы он узнал и понял, что квадратики могут, например, перекрывать друг друга или перемещаться, а уж потом пускать его во "взрослый" мир цветных квадратиков. Думаю, время покажет необходимо ли такое предобучение.
Egg: для данного набора примеров, есть больше, чем одно правило корректного отображения. И так почти в каждом наборе. Интерес в этих задачках представляет синтез правил как таковой. Если программа синтезирует хотя бы одно правильное правило, это уже будет интересно. Все остальные конкретные нюансы самих задач уйдут imho на второй план.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
13:22 23.10.2022 |
|
13:36 23.10.2022 |
|
№5969 |
Это однозначная задача без ошибок.
В файле manual.py подозрительные примеры помечены через TODO. Обычно ошибки видны явно.
Здесь решение задачи такое :-)
class Example_training_1bfc4729(Example):
def convert(self, parser):
mainObj = parser.splitObject()
def getColor(y, x):
ys = [obj.y for obj in mainObj.insides]
return (x in (0, parser.image.shape[1]-1) or y in (0, parser.image.shape[0]-1) or y in ys) * \
mainObj.insides[np.argmin([abs(s-y) for s in ys])].color
return np.fromfunction(np.vectorize(getColor), parser.image.shape, dtype=int)
Короче, результирующее изображение "бинарно" всегда одно и то же, только выкрашено в разные цвета для разных пар.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
13:38 23.10.2022 |
|
13:40 23.10.2022 |
|
№5971 |
Ну не обсуждайте...
У Вас будет тестовое изображение (без результирующей матрицы), то там будет видно, должны ли находиться точки в строках 3 и 8. Т.е. тесты (в паре отсутствует результирующая матрица) тоже могут являться частью обучения.
Поэтому представление, что есть обучающие примеры и отдельно тестовые - вообще говоря, не корректно. В обучающей задаче вопрос о 3 и 8 строках бессмысленнен.
|
| |
create
|
Решатель ARC-задач Франсуа Шолле |
mserg: однозначная задача без ошибок. Egg прав, задача неоднозначная. Допустим в качестве тестового изображения будет картинка А:
После неё должно следовать B или C? И то и другое подойдёт, но правила разные.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Prosolver:mserg: однозначная задача без ошибок. Egg прав, задача неоднозначная. Допустим в качестве тестового изображения будет картинка А:
После неё должно следовать B или C? И то и другое подойдёт, но правила разные.
По условиям конкурса "допустим" не предполагается.
Но если бы в качестве теста в данной задаче была бы картинка A, тогда была бы действительно возникла неоднозначность. "Допустим" приводит нас к выдуманной неоднозначности. Убираем "допустим" - исчезает неоднозначность.
Для обучающих задач корректно использование последней пары для контроля/теста. Как мы видим, вопрос о номерах строк не существует.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Как нас учили, нужно в научных работах нужно избегать изложение от первого лица. Привычка.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Egg:Или вот задачка 5c0a986e
Казалось бы всё хорошо. Но если квадраты расположены на главной диагонали, то какой из хвостов перекрывает какой? Неопределено.
И так с почти каждой.
В какой ситуации возникло расположение квадратов на главной диагонали?
Опять в воображении?
Ситуация как в стихах
Если б я имел коня
Это был бы номер
Если б конь имел меня
Я б наверно помер
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Egg: И все-таки очень странно, что занимаясь поиском отображения не произвольных, а конкретных изображений вы получили 20% решения, вы там ногами программировали или просто груши околачивали?
20% - это лучшее решение на Kaggle, а не мое. Так что ваш вопрос адресован земному шару.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
18:50 23.10.2022 |
|
18:56 23.10.2022 |
|
№5986 |
Egg:mserg: 20% - это лучшее решение на Kaggle, а не мое. Так что ваш вопрос адресован земному шару.
У вас, я полагаю, существенно хуже...
У вас, я полагаю, абсолютный ноль. Но много сообщений на форуме.
----
Насколько я понимаю, несуществующий вопрос неоднозначности задач можно закрыть.
Припоминаю, какие то из задач показались мне неоднозначными. Однако, припоминаю, что по условиям конкурса ответов может 2 - ответ считается подходящим, если подходит хотя бы один из вариантов.
|
| |
write
|
Решатель ARC-задач Франсуа Шолле |
Мне кажется вам нужно на Kaggle - там уровень подходящий. Создайте там тему, спросите сообщество про 20% и про околачивание груш, про зарплату, изложите им своим идеи без предоставления кода. Уверен, там вас оценят.
------
Ладно, загляну через месяц
|
| |
create
|
Решатель ARC-задач Франсуа Шолле |
mserg: вопрос о номерах строк не существует Время покажет. Как мне кажется, вопрос неоднозначности тут один из ключевых.
Ещё мне кажется, мы сходу углубились во второстепенные вопросы, проскальзывая мимо сути. А суть здесь не в задачах как таковых, а в том как их решать правильно. А правильно, это значит с демонстрацией интеллектуальных архитектур, а не своих личных навыков/умений. Предлагаю вернутся к сабжу, если это, конечно, интереснее, чем обсуждение зарплат или околачивания груш.
|
|
|