Friday, September 26, 2025

"Глубокое обучение. Погружение в мир нейронных сетей" Сергей Николенко, Артур Кадурин, Екатерина Архангельская

 


тренировка мозга онлайн (промо)


Заметки:

“следить за последними новостями глубокого обучения лучше всего на arXio (Хранилище научных препринтов arXio, расположенное по адресу http://arxiv.org, содержит статьи по многим научным дисциплинам. Но в некоторых областях arXio стал стандартом де-факто, куда выкладываются практически все серьезные статьи, обычно еще до официальной публикации. К таким областям относится и машинное обучение, в частности обучение глубоких сетей.)”

“Два основных класса задач машинного обучения — это задачи обучения с учителем (supervised learning) и обучения без учителя (unsupervised learning). При обучении с учителем на вход подается набор тренировочных примеров, который обычно называют обучающим или тренировочным набором данных (training set или training sample - тренировочная выборка), и задача состоит в том, чтобы продолжить уже известные ответы на новый опыт, выраженный обычно в виде тестового набора данных (test set, test sample).”

“Задачи обучения с учителем обычно делятся на задачи классификации и регрессии. В задаче классификации нужно поданный на вход объект определить в один из (обычно конечного числа) классов, например, разделить фотографии животных на кошек, собак, лошадей и «все остальное»; или по фотографии человеческого лица понять, кто из ваших друзей в социальной сети на ней изображен. Если продолжать пример с языком, то типичная задача классификации - это разметка слов по частям речи. А в задаче регрессии нужно предсказать значение некоей функ-ции, у которой обычно может быть бесконечно много разных значений. Например, по росту человека предсказать его вес, сделать прогноз завтрашней погоды, предсказать цену акции или, скажем, выделить на фотографии прямоугольник, в котором находится человеческое лицо - сделать это необходимо, чтобы затем эти прямоугольники подать на вход упомянутому выше классификатору.”

“в поисковых и рекомендательных системах часто встречается задача обучения ранжирования (learning to rank). Она ставится так: по имеющимся данным (в поисковой системе это будут тексты документов и прошлое поведение пользователей) отранжировать, расставить имеющиеся объекты в порядке убывания целевой функции (в поисковой системе она называется релевантностью: насколько данный документ подходит, чтобы выдать его в ответ на данный запрос).”

“Типичнейший пример задачи обучения без учителя — это кластеризация (clustering): нужно разбить данные на заранее неизвестные классы по некоторой мере похожести так, чтобы точки, отнесенные к одному и тому же кластеру, были как можно ближе друг к другу, как можно более похожи, а точки из разных кластеров были бы как можно дальше друг от друга, как можно менее похожи. Например, решив задачу кластеризации, можно выделить семейства генов из последовательностей нуклеотидов, или кластеризовать пользователей вашего веб-сайта и персонализовать его под каждый кластер, или сегментировать медицинский снимок, чтобы легко было понять, где же там опухоль. Еще одна часто встречающаяся задача обучения без учителя - это снижение размерности (dimensionality reduction), когда входные данные имеют большую размерность (например, если у вас на входе разбитый на слова текст, размерность будет исчисляться десятками тысяч, а если фотографии - миллионами), а задача состоит в том, чтобы построить представление данных меньшей размерности, которое тем не менее будет достаточно полно отражать исходные данные.”

“И наконец, третий и самый общий класс задач обучения без учителя - задача оценки плотности: нам даны точки данных (х1, …,хм} и, возможно, какие-то априорные представления о том, откуда взялись эти точки, а хочется оценить распределение р(х), из которого они получились. Это очень общая постановка задачи, к ней можно многое свести, и нейронные сети тоже отчасти ее и решают.”

“(Christopher Bishop) Pattern Recognition and Machine Learning [44] … (Kevin Murphy) Machine Learning: A Probabilistic Approach [381] В обеих книгах последовательно описывается вероятностный подход к машинному обучению, основанный на теореме Байеса. Это, по нашему мнению, математи-чески наиболее полный и корректный взгляд на происходящее с обучающимися моделями.”

“в нашем мозге содержится порядка … (сто триллионов) синапсов.”

“на самом деле мы очень плохо понимаем, как работает настоящий человеческий мозг, и искусственные нейронные сети - это не попытка приблизиться к реальной структуре, а достаточно абстрактные модели, созданные для решения оптимизационных задач.”

“В. Гейзенберг. Физика и философия. Часть и целое”

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

“Совместная вероятность - это вероятность одновременного наступления двух событий, р(х, у). Грубо говоря, если есть два кубика, на каждом из которых могут выпасть числа от 1 до 6, то мы можем рассмотреть новую случайную величину «два кубика», у которой будут возможные исходы (1,1), (1,2), (1,3) и так далее до (6, 6), всего 6 × 6 = 36 исходов. Две случайные величины называются независимыми, если: p(x, y) = p(x)p (y).”

“Условная вероятность - вероятность наступления одного события, если из-вестно, что произошло другое, р(х | у); ее обычно определяют формально так: P(x | y) = Р(х, y) / Р(y)”

“теорема Байеса — это основной, центральный инструмент машинного обучения, на ней держатся буквально все рассуждения этой книги и многие другие.”

“математическая модель в машинном обучении обычно представляет собой задание распределения вероятностей на данных и параметрах р(Q, D);”

градиент — это то направление, в котором функция быстрее всего возрастает.”

“Мы уже говорили о том, что большинство задач машинного обучения с учителем можно условно разделить на задачи регрессии, где целевая функция непрерывна, и задачи классификации, где целевая функция представляет собой выбор из нескольких классов; их может быть много (как, например, в распознавании лиц, которое так хорошо делает Facebook), но они все-таки дискретны, и каждому из них должна соответствовать целая область в пространстве параметров. Мы только что выяснили, что для задачи регрессии хорошей функцией ошибки является сумма квадратов отклонений предсказанных ответов от правильных. Эта функция соответствует нормально распределенному шуму, что для непрерывных величин более чем логично.”

“Из теории информации в информатику пришло понятие относительной эн-тропии, или расстояния Кульбака - Лейблера (Kullback - Leibler divergence, KL divergence, relative entropy), названного так в честь Соломона Кульбака и Ричарда Лейблера. Расстояние Кульбака - Лейблера является по своей сути мерой разницы между двумя вероятностными распределениями Р и Q. Как правило, считает-ся, что распределение Р - это «истинное» распределение, а Q - его приближение, и тогда расстояние Кульбака - Лейблера служит оценкой качества приближения.”

“Как использовать расстояние Кульбака - Лейблера для задач классификации? Неформально говоря, мы будем пытаться подсчитать, насколько распределение на тестовых примерах, порожденное классификатором (назовем его д), похоже или непохоже на «истинное» распределение, задаваемое данными (назовем его р).”

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

“Как мы уже знаем, один из самых простых и универсальных методов оптимизации сложных функций - это градиентный спуск. Мы также недавно выяснили, что один из самых простых и универсальных методов задать сложную функцию - это граф вычислений. Оказывается, градиентный спуск и граф вычислений буквально созданы друг для друга! Как вас наверняка учили еще в школе (школьная программа вообще содержит много неожиданно глубоких и интересных вещей), чтобы вычислить производную композиции функций (в школе это, вероятно, называлось «производная сложной функции», как будто у слова «сложный» без этого недостаточно много значений), достаточно уметь вычислять производные ее составляющих”

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

“Как и любая современная библиотека для символьного дифференцирования и/ или обучения нейронных сетей, Tensor Flow может работать как на процессоре, так и на видеокарте. Однако, в отличие от многих других библиотек, Tensor Flow, будучи детищем Google, умеет еще и «из коробки» распараллеливать обучение на распре-деленные кластеры компьютеров”

Тренируй память, читай быстрее, повышай фокус - с приложением для тренировки мозга Readlax. (промо) 

“B Tensor Flow реализован полный набор операций над тензорами из NumPy с поддержкой матричных вычислений над массивами разной формы и конвертирования между этими формами (broadcasting).”

“Вторая библиотека, которой мы часто будем пользоваться в этой книге, - это Keras [79]. Она по сути является «оберткой» над библиотеками автоматического дифференцирования; впрочем, в этой книге мы будем рассматривать их как две отдельные сущности. Keras реализует практически все то, о чем мы будем говорить в этой книге, в виде готовых примитивов: можно сказать «сделай мне сверточный слой с такими-то параметрами», и Keras сделает. Однако «за кулисами» происходит все то же самое, что в Tensor Flow: строится граф вычислений, который Keras потом скормит другой библиотеке для вычисления градиентов и реализации опти-мизационных алгоритмов.”

“Такие «многомерные матрицы» в Tensor Flow называются тензорами; выше мы уже сетовали на то, что тензоры не настоящие, а просто многомерные массивы, но что поделать.”

“дропаут - это слой, который выбрасывает (обнуляет) выходы некоторых нейронов, выбираемых случайно и заново для каждого обучающего примера. Все, что нам сейчас нужно задать - это вероятность их выбрасывания;”

“в Keras есть возможность для каждого слоя добавить регуляризатор на три вида связей: 1)kernel_regularizer - на матрицу весов слоя; 2) bias_regularizer - на вектор свободных членов; 3) activity_regularizer - на вектор выходов.”

“Краткое резюме у данного раздела получается очень простое: для симметричных функций активации с нулевым средним (в основном tanh) используйте ини-циализацию Ксавье, а для ReLU и ему подобных - инициализацию Хе.”

“В классических нейронных сетях нормализация тоже использовалась: благодаря исследованиям [135, 570] известно, что процесс обучения сходится быстрее, когда входы сети «отбелены» (whitened), то есть их среднее приведено к нулю, а матрица ковариаций - к единичной. В этом состоит и идея нормализации в глубоких сетях: если операцию «отбеливания» применять к входам каждого слоя, это позволит избежать проблемы сдвига переменных.”

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

“Скорость обучения — это чрезвычайно важный параметр. Если она будет слишком большой, то алгоритм станет просто прыгать по фактически случайным точкам пространства и никогда не попадет в минимум, потому что все время будет через него перепрыгивать. А если она будет слишком маленькой, то такой проблемы не будет, но, во-первых, обучение станет гораздо медленнее, а во-вторых, алгоритм рискует успокоиться и сойтись в первом же локальном минимуме, который вряд ли окажется самым лучшим.”

“в реальных системах данные часто разбиваются не на две части, а на три: 1) тренировочное множество (training set) используют, как и раньше, чтобы обучать модель; 2) валидационное множество (validation set) применяют, чтобы подгонять ги-перпараметры, вовремя останавливать процесс обучения и т. д.; 3) а третье, тестовое множество (production set) используют уже для окончательной оценки качества.”

“Сверточные нейронные сети (convolutional neural networks, CNN) — это весьма широкий класс архитектур, основная идея которых состоит в том, чтобы переиспользовать одни и те же части нейронной сети для работы с разными маленькими, локальными участками входов. Как и многие другие нейронные архитектуры, сверточные сети известны довольно давно, и в наши дни у них уже нашлось много самых разнообразных применений, но основным приложением, ради которого люди когда-то придумали сверточные сети, остается обработка изображений.”

“Зрительный нерв - толстый пучок аксонов ганглионарных клеток, по которому информация с сетчатки доходит до мозга, - отмечали еще средневековые анатомы, и его важность для зрения была очевидной. Зрительный нерв входит в таламус, отдел мозга, обрабатывающий информацию от органов чувств, там первичная обработка происходит в так называемом латеральном коленчатом теле (lateral geniculate nucleus, LGN), а затем зрительная информация от LGN поступает в собственно зрительную кору (visual cortex).”

"Зрительная кора, как ни странно, расположена сзади, в затылочной доле головного мозга. Она делится на несколько частей, которые обычно незамысловато называются зрительная зона V1 (visual area one), которую также называют стриарной корой или первичной зрительной корой, зрительная зона V2 (visual area two), зрительная зона V3 и т.д., до V6 и V7. Зоны отличаются друг от друга физиологией, архитектурой да и просто обособленным положением в коре, и исследователи не сомневаются, что они различаются и по своим функциям. Хотя на самом деле функциональная специализация зон пока что до конца не ясна, понятно, что функции зон зрительной коры становятся постепенно все более и более общими. По нынешним представлениям: 1) в зоне V1 выделяются локальные признаки небольших участков считанного с сетчатки изображения; это для нас сейчас самое интересное, и об этом мы подробно поговорим ниже; 2) V2 продолжает выделять локальные признаки, слегка обобщая их и добавляя бинокулярное зрение (то есть стереоэффект от двух глаз); 3) в зоне  распознается цвет, текстуры объектов, появляются первые результаты их сегментации и группировки; 4) зона V4 уже начинает распознавать геометрические фигуры и очертания объ-ектов, пока несложных; кроме того, именно здесь наиболее сильна модуляция посредством нашего внимания: активация нейронов в V4 не равномерна по всему полю зрения, а сильно зависит от того, на что мы осознанно или неосознанно обращаем внимание; 5) зона V5 в основном занимается распознаванием движений, пытаясь понять, куда и с какой скоростью передвигаются в зоне видимости те самые объекты, очертания которых выделились в зоне V4; 6) в зоне V6 обобщаются данные о всей картинке, она реагирует на изменения по всему полю зрения (wide-field stimulation) и изменения в картинке вследствие того, что передвигается сам человек;

  1. иногда также выделяют зону V7, где происходит распознавание сложных объектов, в частности человеческих лиц."

“по современным представлениям выходы нейронов из зоны V1 и V2 обрабатываются сразу двумя параллельными путями: вентральный путь из V2 на-правляется к V4 отвечает на вопрос «Что?» (его иногда называют What Pathway), отвечая за распознавание форм и объектов, а дорсальный путь (Where Pathway) отвечает на вопрос «Где?» 1 , проходит из V2 к V5 и V6 и занимается в основном распознаванием движений, управлением движением глаз и рук (особенно если речь идет о том, как достать находящийся в поле зрения объект) [544].”

“Основная идея автокодировщиков столь же проста, сколь и гениальна: давай-те превратим задачу обучения без учителя в задачу обучения с учителем, сами себе придумаем тестовые примеры с известными правильными ответами. И сделаем мы это так: попросим модель обучиться выдавать на выходе ровно тот же пример, который подавали ей на вход! При этом она будет обучаться сначала создавать некое внутреннее представление, кодировать вход какими-то признаками, а потом декодировать их обратно, чтобы восстановить исходный вектор входов.”

“Сверточные сети — это одна из важнейших архитектур нейронных сетей, которая использует сверточные фильтры с общими весами, применяя тем самым одни и те же преобразования к разным частям входа. Такая экстремальная регуляризация позволяет сверточным сетям обучаться очень эффективно и быть весьма глубокими даже «из коробки», а такие более современные идеи, как остаточные связи, позволяют обучать нейронные сети прак-тически неограниченной глубины.”

“главная содержательная проблема рекуррентных сетей — это как добавить в них как можно более долгосрочную память.”

“Задачи первого класса можно условно назвать синтаксическими; здесь за-дачи, как правило, очень хорошо определены и представляют собой задачи классификации или задачи порождения дискретных объектов, и решаются многие из них сейчас уже довольно неплохо, например”

“Второй класс — это задачи, которые в общем случае требуют понимания тек-ста, но по форме все еще представляют собой хорошо определенные задачи с правильными ответами (например, задачи классификации), для которых легко придумать не вызывающие сомнений метрики качества. К таким задачам относятся, в частности: (i) языковые модели (language models): по заданному отрывку текста предсказать следующее слово или символ; эта задача очень важна, например, для распознавания речи (см. чуть ниже); (ii) информационный поиск (information retrieval), центральная задача, которую решают Google и «Яндекс»: по заданному запросу и огромному множеству документов найти среди них наиболее релевантные данному запросу; (iii) анализ тональности (sentiment analysis): определить по тексту его тональность, то есть позитивное ли отношение несет этот текст или нега-тивное; анализ тональности используется в онлайн-торговле для анализа отзывов пользователей, в финансах и трейдинге для анализа статей в прессе, отчетов компаний и тому подобных текстов и т. д.; (iv) выделение отношений или фактов (relationship extraction, fact extraction): выделить из текста хорошо определенные отношения или факты об упоминающихся там сущностях; например, кто с кем находится в родственных отношениях, в каком году основана упоминающаяся в тексте компания и т. д.; (v) ответы на вопросы (question answering): дать ответ на заданный вопрос; в зависимости от постановки это может быть или чистая классификация (выбор из вариантов ответа, как в тесте), или классификация с очень большим числом классов (ответы на фактологические вопросы вроде «кто?» или «в каком году?»), или даже порождение текста (если отвечать на вопросы нужно в рамках естественного диалога).”

“И наконец, к третьему классу отнесем задачи, в которых требуется не только понять уже написанный текст, но и породить новый. Здесь метрики качества уже не всегда очевидны, и мы обсудим этот вопрос ниже. К таким задачам относятся, например: (і) собственно порождение текста (text generation); (ii) автоматическое реферирование (automatic summarization): по тексту породить его краткое содержание, abstract, так сказать; это можно рассмотреть как задачу классификации, если просить модель выбрать из текста готовые предложения, лучше всего отражающие общий смысл, а можно как задачу порождения, если краткое содержание нужно написать с нуля, (iii) машинный перевод (machine translation): по тексту на одном языке породить соответствующий текст на другом языке; (iv) диалоговые модели (dialog and conversational models): поддержать разговор с человеком; первые чат-боты начали появляться еще в 1970-е го-ды, а сегодня это большая индустрия; и хотя вести полноценный диалог и проходить тест Тьюринга пока не удается, диалоговые модели уже вовсю работают (например, первая линия «онлайн-консультантов» на разных торговых сайтах — это почти всегда чат-боты). Важной проблемой для моделей последнего класса является оценка качества.”

“Идея современной модели word2vec состоит практически в том же самом, что и у исходной нейросетевой языковой модели из [40]. Она была предложена Томашем Миколовым с соавторами в работах [123, 137], причем сразу в двух вариантах: в виде непрерывного мешка слов (continuous bag of words, CBOW) и в виде архитектуры skip-gram. Идея word2vec состоит в том, чтобы научиться предсказывать слово из его контекста… или наоборот.”

“процесс разделения потока символов на токены - слова, которые будут потом обрабатываться алгоритмом как единое целое, называется токенизацией.”

“биграмма — это пара слов (w1, w2), которые вместе встречаются аномально часто, то есть совместная вероятность их появления р(w1, w2) в тексте существенно выше, чем р(w1)p(w2), та частота, которую мы бы наблюдали, если бы эти слова появлялись независимо друг от друга.”

“Промежуточный подход был развит в работах от Microsoft Research, посвященных так называе-мым глубоким структурированным семантическим моделям (Deep Structured Se-mantic Models, DSSM) [297, 372]. В качестве нижнего уровня обработки DSSM используют вложения частей слов (subword embeddings), которые представляют слово как набор буквенных триграмм; например, «слово» кодируется как {#сл, сло, лов, ово, во#}.”

“интересным развитием идей вложения частей слов стала работа с участием все того же Томаша Миколова [144], которая стала основой для реализации распределенных представлений слов в очень популярной библиотеке FastText [21, 153]. Идея чрезвычайно проста: давайте использовать все те же идеи word2vec-моделей, но в качестве базовых векторных представлений будем искать представления не слов, а триграмм символов, как в DSSM. Это значит, что мы вводим векторные представления для триграмм символов, затем раскла-дывает каждое слово как сумму векторов его триграмм, а на этих суммах строим обычные конструкции CBOW или skip-gram. Полученные модели обучить проще и быстрее, они не такие большие (мы уже говорили, что триграмм символов куда меньше, чем разных слов), и учет морфологии в них тоже получается почти авто-матически.”

“Задачи машинного обучения, о которых мы говорили до сих пор, можно было разделить на две основные категории: 1) в задачах обучения с учителем (superoised leaming) есть набор «правильных ответов», и нужно его продолжить на все пространство или, точнее говоря, на те примеры, которые могут встретиться в жизни, но которые еще не попадались в тренировочной выборке; к этому классу относятся задачи регрессии и классификации, и этим мы занимаемся в этой книге все время, будь то классификация рукописных цифр по набору данных MNIST или машинный перевод на основе параллельных текстов на разных языках; 2) в задачах обучения без учителя (unsupervised leaming) есть просто набор примеров без дополнительной информации, и задача состоит в том, чтобы понять его структуру, выделить признаки, хорошо ее описывающие; мы подробно рассматривали некоторые задачи обучения без учителя в части III, когда говорили об автокодировщиках и предобучении нейронных сетей, да и вообще, как мы уже много раз видели, одно из главных достоинств глубокого обучения - это именно способность очень умело выделять сложные признаки, раскрывать непростую внутреннюю структуру окружающих нас объектов.”

“Этот подход получил название глубокого обучения с подкреплением (deep reinforcement learning), а сети, обученные таким способом, называются глубокими Q-сетями (deep Q-networks, DQN).”

“центральный метод обучения с подкреплением в роботике - это градиентный спуск по стратегиям (policy gradient) [419].”

“PathNet — это модулярная нейронная сеть, состоя-щая из нескольких уровней, на каждом из которых расположено несколько «мо-дулей», каждый из которых тоже является нейронной сетью. Для разных задач PathNet строит новые пути через эту модулярную архитектуру, то есть для задачи распознавания рукописных цифр может быть использовано другое подмножество модулей, чем для задачи игры в приставку Atari, но модули при этом общие, а разные пути выбираются по сути эволюционным методом, генетическим алгоритмом.”

“17. Bachman P., Precup D. Variational Generative Stochastic Networks with Collaborative Shaping // Proc. 32nd ICML, 2015. - P. 1964-1972.”

“21. Bag of Tricks for Efficient Text Classification / A. Joulin et al. // arXiv, 2016. http:// arxiv.org/abs/1607.01759.

“41. Bengio Y., Thibodeau-Laufer E., Yosinski J. Deep Generative Stochastic Networks Trainable by Backprop // arXiv, 2013. http://arxiv.org/abs/1306.1091.

“44. Bishop C. M. Pattern Recognition and Machine Learning, Springer, 2006.”

“126. Downey A. B. Think Python - How to Think Like a Computer Scientist, O’Reilly, 2012.”

“144. Enriching Word Vectors with Subword Information / P. Bojanowski et al. // arXiv, 2016. http://arxiv.org/abs/1607.04606.

“153. FastText.zip: Compressing Text Classification Models / A. Joulin et al. // ar Xiv, 2016. http://arxiv.org/abs/1612.03651.

“183. Goldberg Y., Levy O. Word2vec Explained: Deriving Mikolov et al.'s Negative-Sampling Word-Embedding Method // ar Xiv, 2014. http://arxiv.org/abs/1402.3722.

“199. GSNs: Generative Stochastic Networks / G. Alain et al. // ar Xiv, 2015. http://arxiv.org/ abs/1503.05571”

“233. Howard R. A. Dynamic Programming and Markov Processes, Cambridge, MA: MIT Press, 1960.”

“234. Hubel D. H. Eye, Brain, and Vision, 2nd edition, W. H. Freeman, 1995.”

“238. Human-Level Control through Deep Reinforcement Learning / V. Mnih et al. // Nature, 2015, vol. 518, no. 7540. - P. 529-533.”

“250. An Introduction to MCMC for Machine Learning / C. Andrieu et al. // Machine Learning, Jan 2003, vol. 50, no. 1. - P. 5-43.”

“297. A Latent Semantic Model with Convolutional-Pooling Structure for Information Retrieval / Y. Shen et al. // Proc. 23rd CIKM, New York, NY, USA: ACM, 2014. - P. 101-110.”

“372. Modeling Interestingness with Deep Neural Networks / J. Gao et al. // EMNLP, 2014.”

“381. Murphy K. P. Machine Learning: a Probabilistic Perspective, Cambridge University Press, 2013”

“419. PetersJ., Schaal S. Reinforcement Learning of Motor Skills with Policy Gradients // Neural Networks, 2008, vol. 21, no. 4. - P. 682-697.”

“423. Pilgrim M. Dive Into Python 3, Berkeley, CA, USA: Apress, 2009.”

“491. Share Z. A. Learn Python the Hard Way: A Very Simple Introduction to the Terrifyingly Beautiful World of Computers and Code, 3rd edition, Addison-Wesley Professional, 2013.”

519. Sutton R. S., Barto A. G. Reinforcement Learning: An Introduction, Cambridge, MA, 1998.

“544. Ungerleider L. G., Mishkin M. Two Cortical Visual Systems // Analysis of Visual Behaviour / Ed. by D. J. Ingle et al., 1982. - P. 549-586.”

“562. Webvision: The Organization of the Retina and Visual System, 2007. http://webvision.med.utah.edu/book/.


Tuesday, September 16, 2025

"Understanding JavaScript Promises" Nicholas C. Zakas

 



Best book notes:

“Each promise goes through a short lifecycle starting in the pending state, which indicates that promise hasn’t completed yet. A pending promise is considered unsettled. The promise in the previous example is in the pending state as soon as the fetch() function returns it. Once the promise completes, the promise is considered settled and enters one of two possible states (see Figure 1-1): 1. Fulfilled: The promise has completed successfully. 2. Rejected: The promise didn’t complete successfully due to either an error or some other cause.”

“An internal [[PromiseState]] property is set to “pending”, “fulfilled”, or “rejected” to reflect the promise’s state. This property isn’t exposed on promise objects, so you can’t determine which state the promise is in programmatically. But you can take a specific action when a promise changes state by using the then() method.”

“Promises also have a catch() method that behaves the same as then() when only a rejection handler is passed.”

“To go along with then() and catch() there is also finally(). The callback passed to finally() (called a settlement handler) is called regardless of success or failure. Unlike the callbacks for then() and catch(), finally() callbacks do not receive any arguments because it isn’t clear whether the promise was fulfilled or rejected”

“JavaScript executed in the regular flow of a program is executed as a task, which is to say that the JavaScript runtime has created a new execution context and executes the code completely, exiting when finished. As an example, an onclick handler for a button in a web page is executed as a task. When the button is clicked, a new task is created and the onclick handler is executed. Once complete, the JavaScript runtime waits for the next interaction to execute more code. Promise handlers, however, are executed in a different way.”

“All promise handlers, whether fulfillment, rejection, or settlement, are executed as microtasks inside of the JavaScript engine. Microtasks are queued and then executed immediately after the currently running task has completed, before the JavaScript runtime becomes idle. Calling then(), catch(), or finally() tells a promise to queue the specified microtasks once the promise is settled. This is different than creating timers using setTimeout() or setInterval(), both of which create new tasks to be executed at a later point in time. Queued promise handlers will always execute before timers that are queued in the same task”

“New promises are created using the Promise constructor. This constructor accepts a single argument: a function called the executor, which contains the code to initialize the promise. The executor is passed two functions named resolve() and reject() as arguments. You call the resolve() function when the executor has finished successfully to signal that the promise is resolved or the reject() function to indicate that the operation has failed.”

“A promise can only be resolved once, so if you call resolve() more than once inside of an executor, every call after the first is ignored.”

“The Promise constructor is useful for creating new promises when you can easily encapsulate settlement behavior inside of an executor. However, there may be times when you want to create a promise and then define its settlement behavior later. You can create a deferred promise using Promise.withResolvers() where the settlement behavior occurs after the creation of the promise rather than during its creation. The Promise.withResolvers() method returns an object with the following proper- ties: 1) promise - an instance of Promise 2) resolve - a function to call to resolve promise 3) reject - a function to call to reject promise”

“The Promise.resolve() method accepts a single argument and returns a promise in the fulfilled state. That means you don’t have to supply an executor if you know the value of the promise already.”

“You can also create rejected promises by using the Promise.reject() method. This works like Promise.resolve() except the created promise is in the rejected state”

“Both Promise.resolve() and Promise.reject() also accept non-promise thenables as arguments. When passed a non-promise thenable, these methods create a new promise that is settled with the same value and state of the settled thenable. A non-promise thenable is created when an object has a then() method that accepts a resolve and a reject argument”

“A promise is a placeholder for a value that may be provided later as the result of some asynchronous operation. Instead of assigning an event handler or passing a callback into a function, you can use a promise to represent the result of an operation. Promises have three states: pending, fulfilled, and rejected. A promise starts in a pending (unsettled) state and becomes fulfilled on a successful execution or rejected on a failure (fulfillment and rejection are settled states). In either case, handlers can be added to indicate when a promise is settled. The then() method allows you to assign a fulfillment and rejection handler; the catch() method allows you to assign only a rejection handler; the finally() method allows you to assign a settlement handler that is always called regardless of the outcome. All promise handlers are run as microtasks so they will not execute until the current script job is complete.”

“Each call to then(), catch(), or finally() actually creates and returns another promise. This second promise is settled only once the first has been fulfilled or rejected.”

“Always have a rejection handler at the end of a promise chain to ensure that you can properly handle any errors that may occur.”

“Another important aspect of promise chains is the ability to pass data from one promise to the next. You’ve already seen that a value passed to the resolve() handler inside an executor is passed to the fulfillment handler for that promise. You can continue passing data along a chain by specifying a return value from the fulfillment handler.”

“Multiple promises can be chained together in a variety of ways to pass information between them. Each call to then(), catch(), and finally() creates and returns a new promise that is resolved when the preceding promise is settled. If the promise handler returns a value, then that value becomes the value of the newly created promise from then() and catch() (finally() ignores this value); if the promise handler throws an error, then the error is caught and the returned newly created promise is rejected using that error as the reason. When one promise is rejected in a chain, the promises created from other chained handlers are also rejected until the end of the chain is reached. Knowing this, it’s recommended to attach a rejection handler at the end of each promise chain to ensure that errors are handled correctly. Failing to catch a promise rejection will result in a message being output to the console, an error being thrown, or both (depending on the runtime environment).”

“The Promise.all() method accepts a single argument, which is an iterable (such as an array) of promises to monitor, and returns a promise that is resolved only when every promise in the iterable is resolved.”

“You’ll want to use Promise.all() in any situation where you are waiting for multiple promises to fulfill, and any one failure should cause the entire operation to fail.”

“The Promise.allSettled() method is a slight variation of Promise.all() where the method waits until all promises in the specified iterable are settled, regardless of whether they are fulfilled or rejected. The return value of Promise.allSettled() is always a promise that is fulfilled with an array of result objects.”

“The result object for a fulfilled promise has two properties: 1) status - always set to the string fulfilled 2) value - the fulfillment value of the promise”

“For a rejected promise, there are also two properties on the result object: 1) status - always set to the string rejected 2) reason - the rejection value of the promise”

“The Promise.allSettled() method can be used in a lot of the same situations as Promise.all(); however, it is best suited for when you want to ignore rejections, handle rejections differently, or allow partial success. Here are some common use cases for Promise.allSettled().”

“The Promise.any() method also accepts an iterable of promises and returns a fulfilled promise when any of the passed-in promises are fulfilled. The operation short-circuits as soon as one of the promises is fulfilled.”

“Promise.any() receives promises that are not fulfilled, and so the returned promise is rejected with an AggregateError. You can inspect the errors property, which is an array, to retrieve the rejection values from each promise”

“The Promise.any() method is best used in situations where you want any one of the promises to fulfill and you don’t care how many others reject unless they all reject.”

“The Promise.race() method provides a slightly different take on monitoring multiple promises. This method also accepts an iterable of promises to monitor and returns a promise, but the returned promise is settled as soon as the first promise is settled. Instead of waiting for all promises to be resolved like the Promise.all() method or short-circuiting only for the first resolved promise like Promise.any(), the Promise.race() method returns an appropriate promise as soon as any promise in the array is settled.”

“The Promise.race() method is best used in situations where you want to be able to short-circuit the completion of a number of different promises. Unlike Promise.any(), where you specifically want one of the promises to succeed and only care if all promises fail, with Promise.race() you want to know even if one promise fails as long as it fails before any other promise fulfills.”

“The async keyword indicates that the following function or method should be made asynchronous. It’s important for the JavaScript engine to know ahead of time if a function is asynchronous because it behaves differently than a synchronous function.”

“Async functions are different from synchronous functions in four ways: 1. The return value is always a promise 2. Thrown errors are promise rejections 3. The await expression can be used 4. The for-await-of loop can be used”

“The await expression is designed to make working with promises simple. Instead of manually assigning fulfillment and rejection handlers, any promise used in an await expression behaves more like code in a synchronous function: the expression returns the fulfilled value of a promise when it succeeds and throws the rejection value when the promise fails. That allows you to easily assign the result of an await expression to a variable and catch any rejections using a try-catch statement.”

“You can also use await at the top level of a JavaScript module outside of an async function. Essentially, a JavaScript module acts as an async function wrapped around the entire module by default. This allows you to call promise-based functions directly, such as using the import() function”

“Using top-level await, you can load modules dynamically alongside the statically loaded modules. (Dynamically loaded modules allow you to construct the module specifier dynamically, as well, which is not possible with static import.)”

“The AbortController class is not defined in ECMA-262, but rather in the DOM Standard⁴. Even so, it has become the standard for cancelling promise-based requests across all JavaScript runtimes. An AbortController object has only two members: 1. abort() - the method to call when you want to abort the request 2. signal - an instance of AbortSignal that encapsulates abort notifications”

“When controller.abort() is called, the promise returned from fetch() is rejected with an error whose name property is “AbortError”. It’s important to check the name property to distinguish abort errors from other types of errors.”

AbortSignal static methods are meant to aid in creating a variety of abort conditions for abortable functions: 1) AbortSignal.timeout() - creates a signal that aborts after an amount of time 2) AbortSignal.any() - creates a signal that combines multiple signals 3) AbortSignal.abort() - creates an already aborted signal”

“The AbortSignal.timeout() method accepts a single argument, which is the number of milliseconds to wait, and returns an instance of AbortSignal that automatically aborts after the milliseconds elapse. You can then pass this instance of AbortSignal to an abortable function without creating an AbortController.”

“The AbortSignal.any() method is similar to Promise.any() in that it is used to combine multiple signals into one, allowing any of the signals to trigger an abort.”

“The AbortSignal.abort() method creates a signal that is already in the aborted state. Similar to the abort() method of an AbortController, there is an optional argument to AbortSignal.abort(), which is the abort reason.”

"The ability to pass an already-aborted signal to an abortable function is helpful when you want to test the behavior of your code when a function is aborted. "

“The AbortSignal class provides more than just static methods for creating new instances; its instances also have two properties that you can use to create your own abortable functions: 1) aborted - a read-only boolean value indicating if the request was aborted 2) reason - a read-only value indicating the reason the request was aborted (either the default AbortError or the argument passed to abort())”

“Each AbortSignal object fires an abort event when the request is aborted, which is helpful if an abortable function performs non-abortable asynchronous operations in its body”

“All JavaScript runtimes track unhandled promise rejections in some way. Web browsers and Deno implement the algorithm specified in the HTML specification while Node.js implements its own solution. Both solutions rely on two events: one that is emitted when an unhandled promise rejection occurs and one that is emitted if a previously unhandled promise rejection has a rejection handler added. The unhandledrejection event is emitted on the globalThis object in web browsers and Deno whenever an unhandled rejection is detected. Event handlers for unhandledrejection receive an event object containing the type of event, the promise that was rejected, and the reason for the promise rejection. The rejectionhandled event is emitted when a previously unhandled promise rejection has a rejection handler added. Event handlers for rejectionhandled also receive an event object that also contains the event type, the promise, and the rejection reason. Node.js also uses two events, but they occur on the process object and have slightly different names: unhandledRejection and rejectionHandled. Event handlers for unhandledRejection receive the rejection reason and the promise as arguments; event handlers for rejectionHandled receive just the promise. Both approaches allow you to implement unhandled rejection reporting for your application by listening for both events and tracking the promises they provide in a separate location. You can then periodically check the list of promises to report them into your logging or reporting system.”

Read 3x Faster (promo) 

"Глубокое обучение. Погружение в мир нейронных сетей" Сергей Николенко, Артур Кадурин, Екатерина Архангельская

  тренировка мозга онлайн (промо) Заметки: “следить за последними новостями глубокого обучения лучше всего на  arXio  (Хранилище научных пр...