Как стать автором
Обновить
21
0
Roman Samarev @rssdev10

Пользователь

Отправить сообщение

Оптимальный вариант сейчас для первого языка — Julia. Простая, современная, без ООП, но с типизацией. При этом синтаксически гибкая и быстрая в исполнении. Самое замечательное то, что на Julia очень легко учить алгоритмам. Хочешь — сортировку пиши, хочешь — кластеризацию векторов, хочешь — операции над тензорами. И всё чисто на Julia. И это не код "только для демонстрации", как в интеграционных языках, а код, который можно реально использовать. Для школьников и студентов она хороша тем, что любые расчётно-иллюстративные работы могут быть сделаны именно на Julia. Причём, переход с неё на низкоуровневые языки не будет вызывать когнитивный диссонанс, как в случае чистых интеграционных языков в качестве первого.

Напрямую в строке — только зная индекс начала UNICODE-символа. Но можно преобразовать её в массив, после чего работать с символами позиционно.


julia> str = "строка"
"строка"

julia> str[2] # пытаемся взять второй байт, а он - часть первого символа
ERROR: StringIndexError("строка", 2)
Stacktrace:
 [1] string_index_err(::String, ::Int64)

julia> collect(eachindex(str)) # получаем все допустимые индексы
6-element Array{Int64,1}:
  1
  3
  5
  7
  9
 11

Однако, можно сделать:


julia> ar = collect("строка")
6-element Array{Char,1}:
 'с'
 'т'
 'р'
 'о'
 'к'
 'а'

julia> ar[2:4]
3-element Array{Char,1}:
 'т'
 'р'
 'о'

Есть пакет https://github.com/JuliaString/Strs.jl и целая группа пакетов для работы со строками https://github.com/JuliaString


julia> using Strs
[ Info: Precompiling Strs [7bddbee9-b4ee-5d4f-bf0b-c84b4398bbf6]

julia> str = Str("строка")
"строка"

julia> str[1]
'с': Unicode U+0441 (category Ll: Letter, lowercase)

julia> str[2:3]
"тр"

Embeddings по структуре довольно простая:
https://github.com/JuliaText/Embeddings.jl/tree/master/src


Собственно, надо реализовать их интерфейс и обернуть код, который я привёл выше в секции Transformers.jl. С этого можно и начать. Дальше, надо будет аккуратно в load_embeddings(BERT) вписать код загрузки наборов данных.


#use wordpiece and tokenizer from pretrain
const wordpiece = pretrain"bert-uncased_L-12_H-768_A-12:wordpiece"
const tokenizer = pretrain"bert-uncased_L-12_H-768_A-12:tokenizer"
const vocab = Vocabulary(wordpiece)

const bert_model = gpu(
  FromScratch ? create_bert() : pretrain"bert-uncased_L-12_H-768_A-12:bert_model"
)
Flux.testmode!(bert_model) 

Ну и, надо поковыряться с lazy-активацией библиотеки Transformers. В общем-то, думаю, всё. Математики там точно нет. Если что, думаю, что товарищи из Transformers помогут.

В StringDistances.jl, в целом, всё хорошо. Но когда размерность сравниваемых списков строк становится большой, придётся делать внешние оптимизации (сортировки и пр.) и, иногда, просто выдёргивать код из библиотеки. Впрочем, то же верно и для обычных Distances.jl. Если же есть множественные сравнения со списками >10^4, то надо хранить промежуточные результаты. Например, в косинусной мере, предварительно подсчитанные нормы для списка векторов, ускоряют вычисление на порядок. Но да, формулу надо разобрать на части.


Embeddings.jl позиционируется как унифицированная библиотека. Но Transformers.jl — более совершенный метод векторизации. И в состав Embedings не входит. К слову, работа по его интеграции технически не сложная. Но вопрос — кто бы за это взялся. Авторы Transformers, когда писали свой пакет, не ориентировались на Embedings.jl вообще. Потому, некоторые части надо немного переписать, а не просто обернуть (подгрузка наборов данных). Но зато, эту работу вполне может осилить начинающий в Julia.
Ну и, в очередной раз обращаю внимание на то, что все Embeddings очень прожорливы к оперативной памяти. 16ГБ — минимально рекомендуемы размер для экспериментов. Меньше — уже будут возникать проблемы. Либо словари будут маленькими, либо перед запуском, надо выгружать всё, включая браузеры.


Ну и на счёт статей, в целом, думаю, что эту статью можно было бы разбить на 4-5 фрагментиков по пакетам отдельно, но методичка нужна сейчас, а времени на подготовку отдельных мелких заметок надо затратить сильно больше. Пока об NLP писать не буду.

Возможно, что просто никто не хотел разбираться. Ставили через https://github.com/JuliaPy/Conda.jl


Но в любом случае, DataFrames быстрее.


PS: Если PyTorch заведётся из под Julia, то это лишний аргумент проектировать продукты именно на Julia, постепенно заменяя питоновские куски.

Нашел стек. Это была машинка с CentOS 7, но что-то подобное я ловил и на MacOS. Именно проблема с некоторыми питоновскими библиотеками в их зависимостях libstdc++. Возможно, это можно исправить полной пересборкой на своей платформе, но кому же эта красота нужна....


Возможно, надо править пути поиска или делать ссылки, но, тоже, не слишком интересное занятие. Тот код просто вычистили от pandas.


Resolving package versions…
loaded
ERROR: LoadError: LoadError: LoadError: InitError: PyError ($(Expr(:escape, :(ccall(#= /home/ml/.julia/packages/PyCall/RQjD7/src/pyeval.jl:39 =# @pysym(:PyEval_EvalCode), PyPtr, (PyPtr, PyPtr, PyPtr), o, globals, locals))))) <class 'ImportError'>
ImportError("/usr/bin/../lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by /home/ml/anaconda3/lib/python3.6/site-packages/pandas/_libs/window.cpython-36m-x86_64-linux-gnu.so)",)
File "/home/ml/.julia/packages/PyCall/RQjD7/src/pyeval.jl", line 2, in const Py_file_input = 257
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/init.py", line 42, in from pandas.core.api import *
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/api.py", line 10, in from pandas.core.groupby.groupby import Grouper
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/groupby/init.py", line 2, in from pandas.core.groupby.groupby import (
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/groupby/groupby.py", line 49, in from pandas.core.frame import DataFrame
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py", line 74, in from pandas.core.series import Series
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/series.py", line 3978, in Series._add_series_or_dataframe_operations()
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/generic.py", line 8891, in _add_series_or_dataframe_operations
from pandas.core import window as rwindow
File "/home/ml/anaconda3/lib/python3.6/site-packages/pandas/core/window.py", line 36, in import pandas._libs.window as _window
Да, я его как раз и пробовал. Но для меня пока и процесс компиляции, и результат оказались довольно тяжеловесными. Надеюсь, что компилятор будет постепенно совершенствоваться.

Это не сложно делается. На тестовом сервисе мы это отработали. Но в статью, пока, не готов оформить. Хотелось бы ещё мелочи с докером и HTTP.jl вычистить. А вообще, разбирались с этим тут: https://discourse.julialang.org/t/packagecompiler-sockets-web-app/28793


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

  • Intel инвестировала в Julia Computing — https://juliacomputing.com/docs/JC_Whitepaper_110417.pdf Поэтому, в ближайшее время, они точно будут поддерживать Julia. Кроме того, у Intel есть специальные сборки Julia именно под их железо. Актуально для резидентов США.
  • Проект с Google — это TPU — https://github.com/JuliaTPU/XLA.jl
  • Машинное обучение — https://github.com/alan-turing-institute/MLJ.jl, Los Alamos National Laboratory — https://lanl-ansi.github.io/software/, https://mads.lanl.gov/
  • Flux.jl с неофициальными бенчмарками, где оказывается сильно быстрее PyTorch по большинству типовых задач. Скорее всего, из-за возможности глубокой автоматической оптимизации. Всё же, один язык для всего — это плюс.

Всё это из того, что на поверхности.


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


Касаемо Питона — ну не знаю, зачем его тащить в научные вычисления. Поддержка библиотек на одном языке всегда дешевле, чем 2-3, что приходится делать с Питоном. Надежда на то, что программисты готовы работать за еду, поскольку их очень много, иногда оправдывается. Иногда выливается в то, что искать толковых становится дороже, чем мигрировать на новую технологию.


А вообще, есть простой вопрос. Если что-то случилось, то кому платить? В случае Julia — платить Julia Computing и они всё исправят. В Питоне — не знаю. Авторы библиотек только за свои куски отвечают. Да и, вообще, не факт, что исправят, потому что процесс принятия решения в Питоне стал слишком тяжеловесным.


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

Мне очень не хватает в Julia простой компиляции в нативный код.

Есть https://github.com/JuliaLang/PackageCompiler.jl
Можно сгенерировать запускаемый бинарник. Только код должен быть оформлен в пакет и быть установлен как пакет. А бинарник будет довольно жирным. Для сервисов — годится.


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

не понмиаю, почему вы так сужаете область применения Julia? У меня она полностью вытеснила python в т.ч. в работе. Julia — это тот же python, тока:

Вы ставите меня в неудобное положение… Если бы я не испытывал симпатии к Julia, то не писал бы обзоры о ней… В то же время, вынужден отметить, что не совсем всё так хорошо.


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


Однако я бы не стал сейчас рекомендовать Julia в веб-разработку. Я стараюсь следить за этой областью, но не видел никаких более-менее надёжных бенчмарков о том, как поведёт себя HTTP.jl на высоких нагрузках. Если же сравнивать скорость разработки и удобство поддержки веб-приложений, например Ruby on Rails и https://github.com/GenieFramework/Genie.jl, то результат, явно не в пользу Genie. Безусловно, всё может измениться, если какая-то крупная компания решит поддержать её, обеспечит разработку массы типовых проектиков, проведёт популяризацию разработки именно на Julia. Но они этого не делают.


У Julia недостаточная интеграция в Java-код. Всё, что связано с "большими данными" — это Java. ETL — преимущественно Java. И если для других языков уже разработаны средства для интеграции с Java, то в случае с Julia — нет. Уже больше года назад я слепил для тестов обвязку https://github.com/rssdev10/julia4j. Бинарная часть собирается руками, потому как перевести на CMAKE и проверить это на разных операционках, руки у меня не дошли. Проблема в том, что embedded Julia не может быть запущена в изолированном контейнере. А значит, даже если мы активируем код из какого-нибудь условного Apache Beam/Flink/Ignite, у нас проблема с запуском на параллельное выполнение кода из разных узлов нашего workflow в рамках одного процесса.


В части интерфейса для Питон-кода. Да, он есть и работает, как минимум для чистого питон-кода. Но проблема в том, что большинство библиотек для Питона — грязные. Например, попытки подключить код, использующий pandas, приведут к ошибке активации его native-библиотек. Понятно, что для Julia-программы pandas не нужен ни в какой форме. У нас есть DataFrames, CSV и пр. с быстрым разбором файлов и быстрыми методами обработки таблиц, но для кода на питоне, использующего pandas это означает, что сначала нам надо его переписать… Даже не представляю, что будет, если пытаться подключить pytorch и как его после этого настраивать на GPU (если кто это проделал — оставьте комментарий). Понятно, что для Julia есть Flux, но кто же будет переписывать на него, например Flair Framework (у Zalando Research нет ни одного публичного проекта на Julia).


И, есть ещё одна серьёзная проблема — люди слишком ленивы, чтобы переключиться на что-то новое просто потому, что оно более быстрое и удобное в перспективе. Я постоянно сталкиваюсь с питонистами, которые меня на полном серьёзе уверяют, что не бывает такого, чтобы в каком-то практическом проекте не нашлось готовой библиотеки для питона. И, даже в принципе, невозможна такая ситуация, что им придётся реализовывать свой вычислительный алгоритм, потому, что такого не бывает… Всё уже сделано до нас и глубоко оптимизировано внутри существующих библиотек (не мои слова)… И речь не идёт о бестолковых студентах. Речь идёт о людях, которые несколько лет занимаются анализом данных… Да и, откровенно говоря, вокруг Питона сейчас зарабатывают очень много денег. На рекламе курсов, на курсах, на продаже учебной литературы и пр. Кто же хочет это всё ломать.


Также, моё личное мнение — Julia — наиболее подходящий язык для обучения программированию. Он простой по синтаксису, позволяет правильно поставить алгоритмическое мышление и избежать зашоренности готовыми библиотеками. А вот будущее Julia сильно зависит от того, на сколько быстро появится поколение, которое выросло именно на Julia.

Если честно, то язык уже не настолько новый, чтобы каждую публикацию начинать с того, для чего он нужен. Посмотрите хаб https://habr.com/ru/hub/julia/


Основная область применения — математика и высоконагруженные вычисления.

Это проблема качества учебных материалов. Впрочем, идти в ROR не зная Ruby — это сложно… Ruby довольно простой язык, но его модель отличается от других императивных языков. И, как раз, попытки тащить предыдущий опыт на Ruby-программирование и вызывают ту самую проблему вхождения и полного непонимания что происходит. Это касается и функционального стиля Ruby, и принципа умолчания в Rails, где просто надо знать, что происходит внутри. Иначе, сплошная непредсказуемая (для новичка) магия.

Главный конкурент, Python, изначально делал ставку на околонаучное сообщество — и выиграл в перспективе

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

julia> a(x::Any, b::Float64) = nothing
a (generic function with 1 method)

julia> a(b::Float64, t::Any) = 1
a (generic function with 2 methods)

julia> methods(a)
# 2 methods for generic function "a":
[1] a(x, b::Float64) in Main at REPL[1]:1
[2] a(b::Float64, t) in Main at REPL[2]:1

julia> a(1.0, 1.0)
ERROR: MethodError: a(::Float64, ::Float64) is ambiguous. Candidates:
  a(b::Float64, t) in Main at REPL[2]:1
  a(x, b::Float64) in Main at REPL[1]:1
Possible fix, define
  a(::Float64, ::Float64)
Stacktrace:
 [1] top-level scope at none:0

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


Possible fix, define
  a(::Float64, ::Float64)

У Julia есть абстрактные и конкретные типы. Абстрактные всегда пустые, но могут наследоваться. Конкретные могут инстанцироваться и иметь поля. Для любого метода можно указать либо тип, к которому его конкретно надо примениять, либо указать, что тип абстрактный, и применять к любому из потомков.


То есть, ответ — полиморфизм статический. Однако методы для обработки наших типов можем писать где угодно. Обычно вместе с определением типа. Шаблоны C++ или Java generics — это, как раз, наиболее близкий аналог.

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


function mag(p::NDimPoint)
    sqrmag = 0.0
    # т.к. размерность точно неизвестна, нужно итерировать по всем полям структуры
    # имена полей для типа T получаются вызовом fieldnames(T)
    for name in fieldnames(typeof(p))
        sqrmag += getfield(p, name)^2
    end
    return sqrt(sqrmag)
end

скорее встретится в форме:


mag(p::NDimPoint) = 
    sqrt(sum(name -> getfield(p, name)^2, fieldnames(typeof(p)))

Или


mag(p::NDimPoint) = 
    sum(name -> getfield(p, name)^2, fieldnames(typeof(p)) |>
    sqrt
Вы не пробовали какие-то замеры делать по производительности именно веб-сервисов?

Не пробовал. О чём честно написал в заключении.

Чтобы результаты были корректные, мало запускать только тесты на Julia. Надо в одинаковых условиях сравнивать с эталонными реализациями.

Идеальный вариант — встраиваться в существующие бенчмарки и прогонять на них.
Если структура декларируется как structure, то она по-умолчанию неизменяема. Если mutable structure, то изменяема. Система типов у Julia несколько отличается от привычных императивных языков. Но довольно простая.

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

А вариант функции с модификацией, когда надо изменить именно тот массив, который передан на вход, автоматически означает, что имя функции будет с суффиксом `!`.
list = [1 2 3]
for item in list
item = item + 1
end


У Julia всё просто. Воспринимаем переменную как ссылку на объект, но помним, что большинство операций не изменяют исходный объект (чистые функции и всё такое....). Соответственно, этот пример — бесполезен. item — локальная переменная. item + 1 не изменяет объект, на который ссылалась item.
julia> list = [1 2 3]
1×3 Array{Int64,2}:
 1  2  3

julia> for item in list
         item = item + 1
       end

julia> list
1×3 Array{Int64,2}:
 1  2  3


А вот такой пример приведёт к модификации (метод с суффиксом `!` является модифицирующим):
julia> list = [[1],[2],[3]]
3-element Array{Array{Int64,1},1}:
 [1]
 [2]
 [3]

julia> for item in list
         push!(item, 1)
       end

julia> list
3-element Array{Array{Int64,1},1}:
 [1, 1]
 [2, 1]
 [3, 1]


И такой пример также приведёт к модификации по той же причине. item ссылается на автономные объекты-массивы, каждый из которых может быть изменён:
julia> list = [[1],[2],[3]]
3-element Array{Array{Int64,1},1}:
 [1]
 [2]
 [3]

julia> for item in list
            item .+= 1
       end

julia> list
3-element Array{Array{Int64,1},1}:
 [2]
 [3]
 [4]


Касаемо оптимизаций Julia на граничных значениях циклов — не отвечу. Скорее всего, что-то делает. В сущности, у неё есть возможность посмотреть генерируемый LLVM-код. По нему можно проверить.
Итерация в for происходит по ссылке или по значению?

Я не совсем понимаю вопрос. Даже в C++ — итератор — отдельный класс. В Julia нет таких языковых понятий как ссылка и значение. Любое присвоение следует воспринимать как копирование ссылки (это не так на уровне машинного кода, но справедливо для модели языка). C-шных операторов &a, *a здесь тоже нет.

Правильно ли я понимаю что если использовать встроенные функции языка типа map, то там проверка индексов опускается?

Опять не понимаю вопрос. Если вопрос о том, выполняется ли реально внутренняя проверка, когда мы пишем a[i], чтобы i не вылетел за пределы, то ответ — не знаю. Скорее всего, не проверяется. Иначе на это придётся тратить процессорное время. Но при работе с итератором всегда идёт вычисление следующего элемента с проверкой границы.

Собственно, в примерах выше, я всего лишь продемонстрировал как избегать использование прямого обращения по индексу, чтобы не ошибиться с границами индексов. Это, конечно, ещё не Руби с его Enumerable. Но уже далеко не C++, с его довольно ограниченными итераторами.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность