Пребывать в тревоге как пишется

Задание 10. правописание приставокиз демоверсии егэ по русскому языку фипи: укажите варианты ответов, в которых во всех словах одного ряда

AVvXsEg rkyNq QnKhnTf2qHvjqim oYvug5VMhaexquSlOH1ZcU7xhoPDTKg0Im61BfBhR4QyZ9d1JIMX95AsCQETmqidRoSDX idvgVh8B qDEylRf8nEFnEDM4vw x0YN6ZdOEzDOU96vf7SrLfNftZDBPnolHL67quj2ng49J1

ЗАДАНИЕ 10. ПРАВОПИСАНИЕ ПРИСТАВОК

Из демоверсии ЕГЭ по русскому языку
ФИПИ:

«Укажите
варианты ответов, в которых во всех словах одного ряда пропущена одна и та же
буква
. Запишите номера ответов.

  1. пр..образовать, пр..неприятный, пр..следовать 
  2. сверх..естественный, с..ёмка, двух..ярусный 
  3.  п..никнуть, пр..дедушка, поз..вчера
  4. чере..чур, и..синя-чёрный, бе..крайний
  5. вз..скать, без..нициативный, сверх..зысканный»

Пояснение (см. также Правило ниже).

Приведём
верное написание.

1) преобразовать (ПРЕ- в значении
«пере-»), пренеприятный (ПРЕ- в значении «очень»), преследовать (ПРЕ-
в значении «пере-»)
 

2) сверхъестественный, съёмка (по
традиции, т.к. С- была приставкой, хотя сейчас выделить значение корня -ём-
трудно), двухъярусный (Ъ пишется в сложных словах с числительными)
 

3) поникнуть (приставки по-
неизменяемая), прадедушка (приставка в значении «первоначальный, наиболее
древний», позавчера (с двумя приставками слово: по- и за-)
 

4) чересчур, иссиня-чёрный, бескрайний
(приставки оканчиваются на -С, потому что корни начинаются с глухого
согласного)

5)
взыскать, безынициативный, сверхизысканный (после иностранных приставок и
приставки СВЕРХ- И на Ы не меняется)
 

Ответ: 124|421|214|412

Источник: Демонстрационная версия ЕГЭ—2019
по русскому языку.

Правило: Задание 10. Правописание
приставок. Обобщение
 

ПРАВОПИСАНИЕ ПРИСТАВОК и написаний, связанных с ними, проверяется в задании
10.

Орфограммы, проверяемые в данном задании:

  •  Не
    изменяющиеся на письме приставки (пункт 10.1.1)
  • Приставки,
    оканчивающиеся на буквы З и С (пункт 10.1.2)
  • Приставки
    ПРЕ и ПРИ (пункт 10.1.3)
  • Приставки
    РАС и РОЗ (пункт 10.1.4)
  • Написание
    разделительного мягкого и твёрдого знака после приставок
    (пункт 10.2.1)
  • Написание
    Ы и И после приставок (пункт 10.2.2)

10.1.1 Не изменяющиеся на письме приставки

1. В большинстве приставок гласные и
согласные, согласно морфологическому принципу русского правописания, пишутся
одинаково, независимо от каких-либо условий:
С-, В-, НАД-, ПОД-, ПРЕД-, ОТ-,
ЗА-, ОТ-,БЕЗ-, ВО-, ВЗО-, ВОЗ, ДО-, НА-, НАД-, НАДО-, О-, ОБ-, ПЕРЕ-, ПО-,
ПОД-, ПРО-, СО-, РАЗО-

ЗАПОМНИТЕ: есть приставка С- (сделать,
сгинуть), но нет приставки
3. 

2. Правописание гласных в приставках в
безударном положении (кроме приставок ПРЕ-, ПРИ-и РАЗ/РОЗ) можно проверить,
подобрав слово, где эта же приставка стоит в ударном положении:

отказать — Отклик, наказать — нАспех.

3. Правописание согласных в приставках (кроме
приставок на 3-, С-) можно проверить, подобрав слово,

где после этой приставки стоит гласная или согласные
В, Л, М, Н, Р:
обходить — оБрастать.

4. Приставка ПРА— употребляется в словах:

прадедушка, прабабушка, прародина, правнук, правнучка, прародитель, праязык, праисторический, пращур.

5. Приставка ПА— встречается только под
ударением:

падчерица, пасмурный, паводок, пасынок, пагубный.

6. Следует различать пары:

ПОдать, ПОдача, ПОданный и ПОДдать,
ПОДдавки, ПОДданный

ПОделка и ПОДделка

ПОдевать и ПОДдевать

ПОдержать, ПОдержанный и ПОДдержать,
ПОДдержанный

ПОдразнить и ПОДдразнить 

НАВЕРХ

10.1.2 Приставки, оканчивающиеся на буквы З и С

Правописание приставок

воЗ-воС

беЗ-беС

иЗ-иС

вЗ-вС

ниЗ-ниС ( не путать с НЕ+С)

обеС-обеЗ

раЗ-раС

чреЗ-чреС

череЗ-череС

которые оканчиваются на 3-, С- и имеют не менее двух
букв определяется последующим согласным.

3 — пишется перед звонким согласным
(
разМышлять)

звонкие согласные: р, л, м, н, й, б, в, г, д, ж, з

С — перед глухим согласным (расСматривать)

глухие согласные: х, ц, ч, щ, к, п, с, т, ш, ф

Эти приставки называют также зависящими от
произношения: в приставке пишем то, что слышим. Под влиянием звонкого звука
корня становится звонким и последний звук приставки, и точно так же, под
влиянием глухого звука корня оглушается приставка. И это звучание отражается на
письме: слышим [раСшум’эт’ца] пишем раСшуметься; слышим [иЗбижат’], пишем
иЗбежать.
 

2. В написаниях типа НЕ+ИЗ+бежный, в
котором две приставки, работает правило приставки НА З/С.

В написаниях типа НЕ+С+гораемый, в котором
две приставки, работает правило написания приставки С.

3. В словах раСчёт, раСчётливый, расчесть
пишется одна С (перед корнем -ЧЕТ-).

В глаголе раССчитать, в причастии
раССчитанный пишутся две С (перед корнем -ЧИТ-).

4. ЗАПОМНИТЬ:

разевать, разинуть, разорить,

бессчётный,

мировоззрение — мировосприятие,

ссора — рассориться,

исступлённо, исподтишка, чересчур

5. Близ — предлог (близ дома).

Но: близстоящий, близсидящий (причастие). 

НАВЕРХ

10.1.3 Приставки ПРЕ и ПРИ

Правописание приставок ПРЕ-/ПРИ- зависит от значения
слова.

Приставка ПРИ- имеет значение:

— приближение (приехать);

— присоединение (приклеить);

— близость (приморский — близко от моря);

— неполное действие (приоткрыть);

— действие, доведённое до конца
(придумать);

— близкое к приставке ДО- (приписывать);

— усиление действия (приналечь);

— действие в собственных интересах
(принарядиться).

Приставка ПРЕ- имеет значение:

— очень (прекрасный — очень красивый);

— близкое к приставке ПЕРЕ- (преодолеть).

В некоторых случаях различение приставок
ПРИ-/ПРЕ- определяется контекстом:

прИбывать в город — прЕбывать в городе;

прИдать вид — прЕдать друга;

прИзреть сироту — прЕзирать недруга;

прИдел (в храме) — прЕдел (терпению);

прИёмник (радиоприёмник) — прЕемник (продолжатель
начатого дела, традиций);

прИвратник (сторож, при вратах) — прЕвратный
(неправильный), но: прИврать (немного соврать)

прИтерпеться (привыкнуть) — прЕтерпевать (пережить);

прИклонить (ветви) — прЕклоняться (перед кем-то);

прИступать (к чему-либо) — прЕступать (через
что-либо);

прИтворить (дверь) — прЕтворить (в жизнь);

прИходящий (приходит) — прЕходящий (непостоянный);

прИпереть (дверь) — прЕпираться (спорить);

прИложить (усилие) — непрЕложный, не подлежащий
изменению;

прИстанище (приют) — беспрЕстанно (не переставая);

прИткнуться (устроиться без удобств)— камень
прЕткновения (помеха, затруднения, =фразеологизм)

прИменьшить (немного)— прЕуменьшить (много,
значительно)

Значение, неясное в существительном
(прилагательном), можно прояснить предыдущим этапом словообразования:

пристанище — пристать, призвание — призвать,
прикладной — прикладывать.

Значения, вносимые в слово приставками
пре-, при-, могут быть объяснены словами или словосочетаниями, близкими по
смыслу: преобразовать — переделать, перестроить; причалить — пристать,
пришвартоваться; прекратить — перестать что—либо делать; пререкаться —
перебивать друг друга, переругиваться; превратное (мнение) перевёрнутое;
неприемлемый — то, что нельзя принять; неприхотливый — без прихотей;
привередливый — человек с большими прихотями, капризами.

 Существуют слова (чаще всего
заимствованные) с приставками ПРЕ-/ПРИ-, значение которых утрачено и

написание нужно запомнить.

ПРИ

ПРЕ

приватный

примадонна

примат

примитив

привилегия

приоритет

приукрасить

привидение

придираться

приключение

присяга

притязание

приобретение

причудливый

приданое

пристрастие

приспособиться

применять

привередливый

приятель

присутствовать

преамбула

престиж

превалировать (преобладать)

превращать

презент

президент

президиум

прелюдия

премировать

премьера

пренебрегать

преобразовать

преодолевать

препарат

препона

препятствие

прерогатива

претендент

претензия

прецедент

 НАВЕРХ

10.1.4 Приставки РАС и РОЗ

В приставках РАЗ- (РАС-) — РОЗ- (РОС-) под
ударением пишется О, без ударения А:
рОсчерк — рАсска-

зать; россказни, но рассказать. Исключение: РОЗыскной
(некоторые источники считают корректным РАЗыскной).

НАВЕРХ

10.2.1 Написание разделительного мягкого и твёрдого
знака после приставок

Ъ пишется:

1) после приставки на согласную перед
буквами Е, Ё, Ю, Я (
подъезд, подъём, предъюбилейный, объявление);

2) в сложных словах, первую часть которых
образуют числительные ДВУХ-, ТРЁХ-, ЧЕТЫРЁХ- (
двухъярусный,

трёхъязычный).

3) После иноязычных приставок:

АД- (адъютант)

ИН- (инъекция)

КОН- (конъюктивит)

ОБ- (объект)

СУБ- (субъект)

ПАН- (панъевропейский)

КОНТР- (контръярус)

ДИЗ- (дизъюнктивный)

ТРАНС- (трансъевропейский)

Ъ не пишется:

1) перед буквами А, О, У, Э (сагитировать,
подоконник, сузить, сэкономить
);

2) в сложносокращённых словах (детясли).

Ь пишется:

1) в корне слова перед буквами Е, Ё, Ю, Я,
И (пьеса, льет, пьющий, рьяный, соловьи);

2) в некоторых иноязычных словах перед О
(бульон, шампиньон).
 

НАВЕРХ

10.2.2 Написание Ы и И после приставок

1. После приставок на гласный пишется И:
проИграть.

2. После приставок, оканчивающихся на
согласную, вместо И пишется Ы:
разЫграть (играть); безЫдейный (идейный)
Запомните приставки, после которых данное правило не работает:

1) с приставками МЕЖ-, СВЕРХ-: межинститутский,
сверхизысканный
;

2) с иноязычными приставками КОНТР-, ДЕЗ-,
ПАН-, СУБ-, ТРАНС-:
контригра;

3) в слове взИматъ (слово-исключение
пишется согласно произношению).

4) От написаний слов с приставками следует
отличать сложные слова типа
пединститут, мединститут, где нет приставки, а,
значит, и нет замены И на Ы.
 

ПРАВОПИСАНИЕ ПРИСТАВОК

и написаний, связанных с ними, проверяется в задании
10.

Орфограммы, проверяемые в данном задании:

 Не
изменяющиеся на письме приставки (пункт 9.1.1)

Приставки,
оканчивающиеся на буквы З и С (пункт 9.1.2)

Приставки
ПРЕ и ПРИ (пункт 9.1.3)

Приставки
РАС и РОЗ (пункт 9.1.4)

Написание
разделительного мягкого и твёрдого знака после приставок (пункт 9.2.1)

Написание
Ы и И после приставок (пункт 9.2.2)

НАВЕРХ 

  • 1 Формулировка
    задания
  • 2 Алгоритм
    выполнения задания
  • 3 Теория. Правописание
    приставок
    • 3.1 Приставки
      на З, С
    • 3.2 Приставки
      ПРЕ и ПРИ 
    • 3.3 Исключения
      и сложности 
    • 3.4 Правописание
      слов с приставками ПРЕ и ПРИ, которое зависит от значения
    • 3.5 Правописание
      слов с приставками ПРЕ/ПРИ, которые нужно запомнить
  • 4 Правописание
    И Ы после приставок
  • 5 Разделительный
    твердый знак (ъ) и мягкий знак  (Ь) . Правописание.
  • 6 Практика 

Мы приехали в Турцию на своей машине из России через Грузию 1.5 года назад. На тот момент единственное, что нам было известно, так это то, что пребывать на своем авто в стране разрешается не более 2 лет. Но не все оказалось так просто…

В Турции на машине с иностранными номерами можно находиться не более 2 лет.

Это действительно так, но есть некоторые нюансы оформления авто, которые вам точно стоит знать, чтобы не попасть на ООООЧЕНЬ большой штраф (как это было у нас).

Наша история

Как правильно ввозить авто на русских/иностранных номерах в Турцию

Какой штраф за нарушение правил?

Страхование автомобиля в Турции

Нюансы вождения автомобиля в Турции

Обязательный пакет документов

Последние новости о продлении авто в 2021 году

Наша история

Итак, мы приехали в Турцию в мае 2019 года и искренне хотели остаться здесь на год, а может и на два. Но через 6 месяцев, абсолютно неожиданно, нас унесло в очередное путешествие по Азии. Сейчас, в период пандемии, о таком можно только мечтать… Ну да ладно, что-то я отвлеклась.

Итак, мы уехали. Машину с чистой совестью оставили на подземной парковке в доме друзей, где она благополучно простояла еще полгода. Итого = 1 год пребывания автомобиля — DONE ✅

Как въехать на машине в Турцию из России

Вернулись в Турцию мы уже в июне 2020 года, куда нас эвакуировало турецкое правительство с просроченным ВНЖ. Эту историю вы можете почитать у меня в Инстаграм, если интересно — https://www.instagram.com/masha.gde/. И вот, что у нас получилось:

  1. Машина год пребывала в стране, где полгода стояла на парковке
  2. У её владельцев просроченное ВНЖ, то есть 2 месяца она вообще сама по себе жила в Турции.
  3. Страховка, которую мы оформляли, просрочилась.

И только в октябре 2020, совершенно случайно (!) мы узнали, что нарушили несколько турецких законов.

  1. Не задекларировали машину при въезде
  2. Не уведомили таможню о том, что уезжаем, а машина остается в стране без нас.

Итог = штраф около 700 долл, который нам любезно скосили на 15% за оперативную оплату.

Как правильно ввозить авто на русских/иностранных номерах в Турцию

  1. Ваш срок пребывания в Турции должен быть не более 185 дней.

Объясню на примере:

Вы прилетели в Турцию на самолете и вам здесь так понравилось, что решили оформить ВНЖ, остаться на год или 2…

Как оформить ВНЖ самостоятельно я уже писала.

… и привести сюда свое авто. Так вот, у вас есть не более 185 дней, чтобы вернуться в Россию и привести машину. Если вы в Турции находитесь более 185 дней, то вам нужно выехать из страны на 6 месяцев (185! дней) и только тогда вы можете заехать обратно на автомобиле.

Если вы приехали на автомобиле, то авто должно пребывать на территории страны безвыездно в течение 2 лет. Выезжаете за границу (например, Европа или Грузия)? Снова здорова — полгода (185 дней) нужно ждать, чтобы заехать обратно на этом же авто.

Но говорят есть лазейка: можно переоформить владельца ПТС со сменой номеров и этот новый владелец снова может заехать на той же машине.

И снова НО: новоиспеченный владелец не должен пребывать в Турции более 185 дней. То есть мой муж не сможет переоформить документы на меня, потому что мне в этом случае нужно где-то переконтоваться полгодика.

  1. Страховка. Да, вам обязательно понадобится страховка. Её можно оформить на границе Грузия/Турция или заранее в России GreenCard. К сожалению, я уже и не помню, сколько мы платили за страховку на границе, но экономически выгодно были оформить её именно там.
  2. Обязательно необходимо задекларировать авто. Это сделать можно, например, на Таможне в Аэропорту Анталии. Уверена, таможенные офисы есть и в других городах, поищите на карте. И чем быстрее вы это сделаете, тем лучше.
  3. При выезде на любой срок владелец авто (именно тот, кто официально въезжал) обязан за 1 сутки прийти в таможню и написать заявление о том, что его машина будет стоять на стоянке (указываем адрес, где) и никто на ней ездить не будет.

Именно из-за отсутствия того самого злополучного заявления мы и получили свой штраф (нам даже отсутствие декларации простили!). Мне даже не верится, что такая мелочь, как писулька от руки, может стоит 500 долл.

Какой штраф за нарушение правила?

Размер штрафа зависит от автомобиля. У них на таможне есть сложная формула, по которой они все это считают. Там задействована и средняя цена авто, и объем двигателя.

УФ! Хорошо, что у меня не БМВ ?

Заявление пишется именно для того, чтобы уверить турецкие власти, что никто не будет ездить на авто в ваше отсутствие.

Как въехать на машине в Турцию из России

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

То есть, имейте в виду, самый большой грех — это кататься на чужой машине в отсутствие владельца в стране. Настоящее ТАБУ!

Кстати, штраф можно было оспорить в суде. Но наша ситуация была такая нестандартная (просроченное ВНЖ, отсутствие декларации и бла-бла-бла), что по факту можно было бы огрести по полной программе вплоть до депортации. Поэтому мы смирились с судьбой, заплатили 500 долл и теперь спим спокойно.

За отсутствие декларации за прошлый год нам штраф не выписывали.

Страхование автомобиля в Турции

У меня об этом есть очень полезная статья. Здесь я рассказываю, как мы это делаем самостоятельно, сколько стоит, а также делюсь адресом страховой компании. Дело в том, что НИКТО не хотел нам оформлять страховку на русские номера в Турции… муж объехал практически все компании.

Свою вторую страховку на следующий год мы сделали там же. Но на этот раз настаивали, чтобы и меня туда вписали. НАСТАИВАЛИ — потому, что второе имя вписывать отказывались и в итоге так и не вписали (в страховом полисе стоит имя мужа). Но менеджер вбил мои данные в систему и уверил, что если я буду за рулем и меня остановят, то полицейский при проверке это увидит.

Вот еще несколько аргументов, которые приводили в страховой:

  1. Жена может водить автомобиль мужа (апостиля у нас нет)
  2. Иностранцев вообще не останавливают — это правда
  3. А ничего страшного — чисто турцкое, не образайте внимание )

Нюансы вождения автомобиля в Турции

Как часто останавливают машины с российскими номерами? За 1.5 года нас останавливали один раз и то случайно. Бедный жандарм так испугался, когда увидел русские номера, что даже права при проверке держал вверх ногами.

Камеры нарушения за превышения скорости практически не фиксируют, точнее — не фиксировали до момента декларации автомобиля. После декларации нас, очевидно, внесли в систему и теперь иногда приходят мелкие штрафы.

Штрафы в Турции можно посмотреть здесь: https://webihlaltakip.kgm.gov.tr/WebIhlalSorgulama/Sayfalar/Sorgulama

Стиль вождения в Турции… нормальный. Если вы водили авто в Грузии, то вообще отличный!

Обязательный пакет документов

Итак, еще раз для тех, кто собирается кататься на автомобиле в Турции 2 года:

  1. Страховка на машину (годовая! на время действия вашего ВНЖ)
  2. Задекларировать автомобиль (номера вбивают в систему)
  3. Не забыть написать заявление, если вы уезжаете из страны (даже на 1 день)
  4. Международные права и российские права

Когда мы катались на машине по Турции по штампу в паспорте (без ВНЖ), то декларировать авто не нужно было. По крайне мере, на границе с Болгарией проблем из-за её отсутствия не было.

Последние новости о ввозе и продлении авто в 2021 году

Согласно закону от 2020 года правительство Турции разрешило всем, у кого срок пребывания авто был просрочен из-за закрытия границ, остаться в стране до 31 декабря 2020 года. В 2021 году продлить срок пребывания всем, у кого он просрочен, можно на таможне — дают от 2 месяцев. Но имейте ввиду, что в Турции все по-турецки и многое зависит от чиновника.

Так, в Анталии продление давали без проблем, а в Алании говорили — извините, уезжайте как хотите.

На январь 2021 год ввести машину можно только через Одессу, так как граница с Грузей закрыта. Еще как вариант — паром через Черное море.

БОНУС!

С Российскими и Международными правами в Турции можно ездить только полгода. Потом права становятся недействительными и при любой спорной ситуации на дороге вы будете виноваты как человек без прав!

Что делать в этой ситуации, я расскажу в следующей статье.

Я буду очень рада, если вы дополните статью в комментариях. Потому что законы постоянно меняются и неизвестно, что будет даже завтра! Если я где-то ошиблась, поправьте меня в комментариях).

Как мы путешествуем по миру:

Ищем дешевые билеты на Aviasales

Бронируем квартиры на airbnb.com

Мой блог в Инстаграм

https://www.instagram.com/masha.gde/ — Подписывайтесь!

JavaScript — это фантастический язык для серверного программирования, так как он поддерживает асинхронное выполнение кода. Но это ещё и усложняет его использование.

Асинхронность означает возможность написания кода, который не подвержен обычным проблемам, связанным с потокобезопасностью, и поддерживает выполнение множества задач в «параллельном» режиме. Например — ожидание считывания файла не блокирует другие вычисления.

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

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

Эти соединения что-то захватывало.

Речь идёт о кодовой базе в миллионы строк кода, которой в течение нескольких лет занимались десятки программистов. Может — проблема заключается в неразрешённых промисах? Я решил исследовать этот вопрос.

Проблема останова поднимает голову

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

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

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

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

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

Но «убийство» процесса сервера при каждом недоразумении — это решение не идеальное. И, кроме того, оснащение каждого промиса в кодовой базе тайм-аутом — это, в лучшем случае, очень сложно.

Но не так всё плохо! Проблему останова можно решить для подмножества распространённых паттернов.

Паттерны проблемных промисов

В прочитанной мной публикации «Нахождение проблемных промисов в асинхронных JavaScript-программах» Алимадади с соавторами выделили распространённые паттерны, которые приводят к появлению неразрешённых промисов, и представили программу PromiseKeeper. Эта программа находит потенциально неразрешаемые промисы с использованием графа промисов.

▍Краткий рассказ о JavaScript-промисах

Та публикация начинается с обзора JavaScript-промисов.

Промисы представляют асинхронные вычисления и могут пребывать в трёх состояниях: pending (ожидание), fulfilled (успешное разрешение) и rejected (отклонение). Вначале они оказываются в состоянии pending.

Код, реагирующий на результат работы промиса (обработчик определённого события), регистрируют с помощью метода .then().

// Немедленно разрешается со значением 17
const promise = Promise.resolve(17)
promise.then(
  function fulfilledReaction(value) {
    console.log({ value })
  },
  function rejectedReaction(error) {
    console.log({ error })
    throw error
  }
)

В коде, на практике, опускают второй параметр для того чтобы сосредоточиться на коде, который выполняется при успешном разрешении промиса (fulfilledReaction). Обычно так поступают при построении цепочек промисов:

// Немедленно разрешается со значением 17
const promise = Promise.resolve(17)
promise.then(value => value + 1)
       .then(value => value + 1)
       .then(function (value) => { console.log(value) })

Каждый вызов .then() приводит к созданию нового промиса, который разрешается возвращаемым значением кода, реагирующего на завершение работы предыдущего промиса. Обратите внимание на то, что последний вызов неявно разрешается со значением undefined. Дело в том, что в JavaScript функция, которая не возвращает что-либо, неявным образом возвращает undefined.

Важная деталь.

В цепочку промисов можно добавить механизм обработки ошибок с использованием .catch():

// Немедленно разрешается со значением 17
const promise = Promise.resolve(17)
promise.then( ... )
       .then( ... )
       .then( ... )
       .catch(err => ...)

Каждый промис, созданный .then(), неявно определяет код, реагирующий на отклонение промиса, аналогичный конструкции err => throw err. Это значит, что .catch() в конце цепочки промисов может отреагировать на ошибки, возникшие в любом из предыдущих промисов.

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

// Немедленно разрешается со значением 17
const promise = Promise.resolve(17)
promise
  .then(undefined) // используется стандартный механизм value => value
  .then((value) => console.log(value))

Полагаю, подобное чаще происходит случайно, чем намеренно.

Промисы можно связывать, используя один промис для разрешения другого промиса:

const p0 = Promise.resolve(17) // Немедленно разрешается
const p1 = Promise.reject("foo") // Немедленно отклоняется
p0.then(function (v) {
  return p1
})

Здесь состояние промиса p0 связано с p1. То есть — неименованный промис, созданный в строке 3, отклоняется со значением foo.

В реальном коде можно видеть много подобных конструкций. Часто они устроены не так понятно, как эти.

▍Паттерн №1: необработанное отклонение промиса

Обычным источником ошибок при работе с промисами являются необработанные отклонения промисов.

Это происходит, когда неявно отклоняют промис, выдавая ошибку в коде, реагирующем на успешное завершение промиса:

promise.then(function (val) {
  if (val > 5) {
    console.log(val)
  } else {
    throw new Error("Small val")
  }
})

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

Исправить это можно, использовав метод .catch():

promise
  .then(function (val) {
    if (val > 5) {
      console.log(val)
    } else {
      throw new Error("Small val")
    }
  })
  .catch((err) => console.log(err))

Теперь у нас появляется возможность обработать ошибку.

Но мы не перевыбросили эту ошибку! Если другой промис, связанный с этим, или объединённый с ним в цепочку, полагается на этот код, ошибка будет оставаться «проглоченной». Код продолжит выполняться.

Попробуйте следующее в консоли браузера:

const p = Promise.resolve(17)
p.then(function (val) {
  throw new Error("Oops")
  return val + 1
})
  .catch(function (err) {
    console.log(err)
  })
  .then(function (val) {
    console.log(val + 1) // prints NaN
  })

Можно ожидать, что в этом коде выполняются вычисления 17 + 1 = 18, но, из-за неожиданной ошибки, мы получаем NaN. Промис, неявно созданный .catch(), неявно разрешается (а не отклоняется) со значением undefined.

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

▍Паттерн №2: незавершённые промисы

Новые промисы пребывают в состоянии pending до тех пор, пока не будут успешно разрешены или отклонены (то есть — завершены). Но если промис не завершается, его можно назвать остановившимся промисом. Он навсегда останется в состоянии pending, не давая выполняться коду, полагающемуся на его завершение.

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

Авторы вышеупомянутой публикации показывают пример проблемы из node-promise-mysql, где connection.release() возвращает промис, который никогда не разрешается.

Этот пример сложно свести к нескольким строкам кода, поэтому вот — кое-что попроще:

const p0 = new Promise((resolve, reject) => null)
const p1 = Promise.resolve(17)
p0.then((result) => p1)
  .then((value) => value + 1)
  .then((value) => console.log(value)) // ожидается 18

Последний промис соединён цепочкой из .then() с промисом p0, который никогда не разрешается и не отклоняется. Этот код может выполняться вечно, но он никогда не выведет никакого значения.

Это, опять же, простейший пример, но представьте себе огромную кодовую базу, над которой работают десятки программистов. Совсем неочевидным может быть то, что функция в некоторых случаях бесконечно долго держит некий промис в состоянии pending.

▍Паттерн №3: неявные возвраты и код, реагирующий на результаты работы промиса

Цепочка из промисов прерывается без выдачи каких-либо ошибок в том случае, если разработчик забывает о включении в код выражения return.

Эта проблема похожа на ту, связанную с «проглатыванием» ошибок, о которой я уже рассказывал. Вот фрагмент кода из Google Assistant, который приводят Алимадади с соавторами:

handleRequest (handler) {
    if (typeof handler === 'function') {
        const promise = handler(this)
        if (promise instanceof Promise) {
            promise.then(result => {
                debug(result)
                return result
            }).catch(reason => {
                this.handleError('function failed')
                this.tell(!reason.message ? ERROR_MESSAGE : reason.message)
                return reason
            })
        }
    }
}

Метод handleRequest() использует объект Map с обработчиками, предоставленными разработчиком, для организации асинхронной работы с запросами Assistant. Объект handler (обработчик) может быть либо коллбэком, либо промисом.

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

Но все эти возвраты выполняются внутри кода, реагирующего на разрешение или отклонение промиса. Промис не осуществляет возврата значения. Результат реакции на промис handler теряется.

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

Поиск антипаттернов с помощью графа промисов

Алимадади с соавторами создали программу PromiseKeeper, которая динамически анализирует кодовую базу на JavaScript и рисует графы промисов.

Граф промисов

Мне не удалось запустить эту программу, поэтому передам то, о чём рассказали авторы публикации.

▍Графы промисов

Асинхронный код можно представить в виде графа, вершины которого (промисы, функции, значения, механизмы синхронизации) соединены рёбрами (разрешение промиса, регистрация обработчиков, связь, return или throw).

  • Вершины-промисы (p) представляют собой случаи запуска промисов.
  • Вершины-значения (v) представляют значения, с которыми разрешаются или отклоняются промисы. Это могут быть функции.
  • Вершины-функции (f) — это функции, зарегистрированные для обработки разрешения или отклонения промиса.
  • Вершины-механизмы синхронизации (s) представляют собой все использованные в коде вызовы Promise.all() или Promise.race().
  • Рёбра разрешения или отклонения промиса (v)->(p) указывают на связи вершин-значений с вершинами-промисами. Они помечены как resolve или reject.
  • Рёбра регистрации обработчиков (p)->(f) указывают на связи между промисом и функцией. Они помечены как onResolve или onReject.
  • Рёбра связей (p1)->(p2) показывают взаимоотношения между связанными промисами.
  • Рёбра return или throw (f)->(v) показывают связи функций и значений. Они помечены как return или throw.
  • Рёбра механизмов синхронизации указывают на связи, идущие от множества промисов к одному механизму синхронизации промисов. Они помечаются как resolved, rejected или pending на основании того, как ведёт себя новый промис.

Вот аннотированная версия вышеприведённого графа.

beo3RPTAZulNEa80tRBZ7Z 3RyDdXs44oIJ6plwoJZlZEMxdgKRG3mJ s9lzrEtaZQ4bt92m3N6PdEHdQrEwYKJJ1EjWOR8PM33KAYySMFIN5lOQ ypmYbFsvUXF7YRFJBegvGky

Аннотированный граф промисов

▍Использование PromiseKeeper для поиска анти-паттернов

Программа PromiseKeeper направлена на конструирование и визуализацию графа промисов кода. Она анализирует код динамически, по мере выполнения тестов. Динамический контекст выполнения кода позволяет находить анти-паттерны, которые не видны при анализе самого кода.

Вот на какие анти-паттерны обращает внимание программа:

  • Пропущенные обработчики отклонения промисов — это ведёт к «проглатыванию» ошибок.
  • Попытки многократного завершения работы промисов — это происходит, когда пытаются разрешить или отклонить промис, работа которого уже была завершена.
  • Незавершённые промисы, то есть такие, которые не разрешены, но и не отклонены в то время, когда PromiseKeeper строит граф.
  • Недостижимые обработчики — то есть код, зарегистрированный для обработки разрешения или отклонения промисов, который не выполняется во время динамического анализа кода, выполняемого PromiseKeeper.
  • Неявные возвраты и стандартные обработчики — это может привести к неожиданному поведению промисов, расположенных ниже в цепочке промисов.
  • Ненужные промисы — когда намеренно создают новый промис в функции, которая уже обёрнута в промис.

Так как PromiseKeeper полагается на динамическое выполнение кода — качество анализа напрямую зависит от того, насколько хорошо код покрыт тестами. В ходе этого анализа нельзя исследовать невыполненный код, что может вести к ложноположительным результатам.

Реализация PromiseKeeper основана на фреймворке для динамического анализа JavaScript-кода Jalangi. В нём имеются коллбэки, которые реагируют на события жизненного цикла промисов.

Мне не удалось заставить работать PromiseKeeper на моём компьютере, но Алимадади с соавторами сообщают о том, что не вполне благополучные промисы встречаются почти во всех кодовых базах, в которых используется JavaScript.

77c7fb53726b48d333344bdd386b9fcb

Отчёт по выявленным анти-паттернам

Интересно то, что 1012 экземпляров незавершённых промисов встречаются всего в 17 местах кода Node Fetch.

Авторы сообщили, что в ходе их эксперимента 43% промисов оказались неразрешёнными. Это, вероятнее всего, указывает на неполноту тестов, а не на то, что популярные программные продукты, которые они исследовали, безнадёжно «поломаны».

Что делать?

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

Мы, решая проблемы промисов, добились серьёзных успехов, логируя сведения о необработанных отклонениях промисов, снабжённые полной трассировкой стека. Делается это с помощью такого кода:

// Выводит полезное сообщение об ошибке когда
// отклонение промиса оказывается необработанным
process.on("unhandledRejection", (err, promise) => {
  const stack = err instanceof Error ? err.stack : ""
  const message = err instanceof Error ? err.message : err

  Logger.error("Unhandled promise rejection", {
    message,
    stack,
    promise,
  })
})

Ещё можно попробовать Node.js-модуль async_hooks, который позволяет наблюдать за жизненным циклом промисов, и попытаться выявлять промисы, которые выполняются слишком долго. Можно, например, сравнивать время работы промиса с заданным тайм-аутом и выводить в консоль предупреждение.

У меня была интересная попытка использования async_hooks для выявления промисов, выполняющихся слишком долго, но особого толку из этого не вышло. Нельзя получить ссылку на контекст выполнения (только — C-указатель). То есть — можно увидеть, что что-то работает медленно, но о том, что это такое, узнать нельзя.

Меня привлекает идея превращения PromiseKeeper в нечто вроде плагина для Jest. Представьте себе, что после каждого запуска тестов формируется граф промисов, вроде того, который вы здесь видели.

Сталкивались ли вы с проблемами, вызванными неправильной работой промисов?

oug5kh6sjydt9llengsiebnp40w

Как составить акт приема-передачи гаража в 2021

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

После того, как сделка оформлена, акт должен направиться в бухгалтерский отдел. Здесь у арендатора происходит оприходование недвижимости. Арендодатель также должен гараж списать с баланса своей организации.

Акт состоит из 3 частей: шапки, основной части и заключительной.

Шапка

Здесь размещают такие сведения:

  • о том, что акт — это приложение к договору купли-продажи гаража, указывают реквизиты документа;
  • наименование акта и его номер;
  • место подписания;
  • день подписания.

Основная часть

Эта часть начинается с информации о сторонах сделки (продавце и покупателе, арендодателе и арендаторе). Указывают наименование компании, если в сделке участвует юридическое лицо, или ФИО — если физическое (гражданин или ИП). От лица организации может выступать генеральный директор, именно он будет ставить подписи на договоре и акте, поэтому его ФИО тоже нужно указать. Затем вносят данные о договоре аренды или купли-продажи, приложением к которому является акт: это номер и дата подписания.

Далее начинают описание объекта сделки, то есть гаража. О нем вносят такую информацию:

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

Заключительная часть документа

Завершают акт сведения о том, что покупатель (арендатор) не имеет претензий к продавцу (арендодателю), касающихся состояния гаража, а продавец (арендодатель) к покупателю (арендатору) — в отношении расчета по сделке. Также указывают, что все документы и данные в них сверены, деньги уплачены в кассу организации или переведены на счет, документ составлен в 2 экземплярах и оба они равнозначны в юридическом плане.

В конце обе стороны ставят свои подписи и их расшифровки.

Образец акта приема-передачи гаража

Акт приема-передачи гаража — Правильный образец в 2021

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

Затем должна идти основная часть, которая содержит такую информацию:

  1. Сведения об обеих сторонах. Если это ИП или физические лица, достаточно указать ФИО и паспортные данные. Что касается организаций, указываются данные их регистрационных документов, сведения о руководителях.
  2. Нужно подробно описать гараж, который является объектом сделки. Здесь указывается количество машин, на которое он рассчитан, общая площадь, адрес расположения. Также нужно прописать различные подробности, например, имеется ли здесь отопление, в каком состоянии электрика, присутствует ли здесь смотровая яма.
  3. Если у недвижимости имеются какие-то недостатки, их также нужно подробно описать. Благодаря этому стороны не смогут предъявлять друг другу претензии.
  4. Не исключено, что в гараже присутствует какое-то оборудование, мебель, другие вещи. Их также необходимо упомянуть в акте.
  5. Если к недвижимости прилагаются какие-то документы, которые передаются получателю, они также должны быть отображены в акте.

Условия заключения сделки

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

Естественно, что продающей стороне необходимо иметь полную дееспособность. В противном случае сделка по отчуждению гаража легко оспаривается в судебном порядке. Теперь перейдём непосредственно к условиям договора.

На что нужно обратить внимание

Когда продавец получил в своё распоряжение гараж, будучи в браке, то он переходит разряд общей совместной собственности. А раз так, то его продажа требует согласия от второго супруга (нотариального). Игнорирование этого правила приведёт к недействительности договора.

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

Расписка по предоплате

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

Она составляется продавцом в свободной форме от руки, но должна включать следующую обязательную информацию:

  • название документа;
  • ФИО и паспортные данные продавца и покупателя, включая прописку/регистрацию;
  • факт получения денег и передаваемая сумма, прописанная цифрами, а в скобках – прописью;
  • детальная опись приобретаемого объекта и его технические характеристики: номер, название кооператива, площадь и размеры, место расположения, материал изготовления и другие подробности;
  • номер и дата заключения договора, в котором прописаны условия, в том числе, информация о том, когда объект перейдет в собственность покупателя;
  • подтверждения того, что продавец не имеет никаких претензий к покупателю;
  • подпись продавца с расшифровкой и датой составления документа.

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

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

Не очень. Информация устарела.

17.88%

Затрудняюсь ответить …

5.96%

Проголосовало: 151

Nomeroff Net. Как ускорить распознавние номерных знаков.

После запуска моделей на прод рано или поздно приходит понимание того, что Ваши сервисы популярны и что KPI растут. Вместе с популярностью приходят тормоза и нестабильность. В этой статье речь пойдет о прикладном аспекте оптимизации быстродействия алгоритмов/моделей на примере движка распознавания автомобильных номеров “Nomeroff Net”. Буду делиться опытом, полученным на протяжении 2-х летней разработки. Если коротко: нам удалось ускорить время распознавания 1 фото более чем в 10 раз.

“Чел догадался в свой сервер вставить RTX 3090” подумаете Вы… Приблизительно так и было, только если взять замеры до установки GPU то все ускорили в 100+ раз :).
Не будет детального описания архитектуры моделей (они давно известны в узких кругах), хочу поделиться важными моментами, на которые стоит обратить внимание при оптимизации ваших ML-сервисов.

Коротко о проекте

Я работаю в компании RIA.com. Самым большим проектом компании является онлайн классифайд AUTO.RIA.com. В день мы анализируем до 200 000 фото, на которых, в том числе, находим и считываем номерной(ые) знак(и). В 2018-2019 годах мы не нашли хорошего opensource-решения по распознаванию номеров, которое умело справляться с этой задачей. Основные проблемы, которые плохо решаются: чтение exUSSR-номеров с кириллицей, чтение номеров, которые сфотографированы “под наклоном” или с “перспективными искажениями”, чтение многострочных номеров (часто используются на спецтехнике, (мото/квадро)циклах, тракторах и так далее. В результате мы запилили небольшой движок под названием Nomeroff Net, с помощью которого научились полностью или частично вышеописанные проблемы решать. Протестировать как это работает можно прямо сейчас (сразу оговорюсь, что многострочные номера пока что читаются неважно)

Коротко об авторах

Сейчас основными разработчиками проекта являются Дмитрий Пробачай (
dimabendera ) и я (apelsyn), кроме того после публикации кода в opensorce на github нам оказали неоценимую помощь многие хабралюди: присылали датасеты, находили баги, писали отзывы и пожелания. Мы все читаем и стараемся на все отвечать. С радостью поможем вам натренировать датасет для Вашей страны, если в списке OCR-ок ее пока что нет.

Где обычно тормозит

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

06782d7b656a1471a37b19c0bc8bb3ae

При разработке “тяжелых” моделей мы часто наблюдали картину, при которой мы не могли загрузить GPU “на все деньги” по причине медленной передачи данных в видеопамять. Для более глубокого понимания проблемы рекомендую прочитать статью Dmitriy Vatolin 3Dvideo “Аппаратное ускорение глубоких нейросетей: GPU, FPGA, ASIC, TPU, VPU, IPU, DPU, NPU, RPU, NNP и другие буквы”. Неожиданным тормозом может стать устройство хранения, на которое поступает контент для инференса. Обязательно обращайте внимание на iowait системы, где происходит загрузка/предпроцессинг фото, иначе может случиться так, что ваше приложение будет пребывать в бездействии, вызванном ожиданием операций ввода/вывода больше чем, собственно, сам инференс! Очень часто это неочевидно. Рядом с запущенными инстансами инференса в контейнерах или виртуалках может находиться контейнер с БД, которая утилизирует все ресурсы дисковой системы. Не будем погружаться в тему быстродействия железа, так как вижу что некоторые из вас начинают засыпать.

Предположим, что все «железячные» проблемы в вашей системе решены и мы приступаем к оптимизации приложения.

С чего мы стартовали

Первая версия-прототип показывала скорость детекции в районе 7s/фото (Без GPU и на весьма средненьком железе), поэтому этот замер не учитываем, начнем c того что пошло в продакшн на раннем этапе 840ms/фото. А хотелось меньше 100ms.

Ускоряем загрузку фото

Казалось бы, загрузка фото — это ж милисекунды, что тут оптимизировать? Не спешим с выводами, посмотрим на примере популярной библиотеки Pillow. Пробуем загрузить фото и получить матрицу для дальнейшей обработки нашей потенциальной моделью — пишем как-то так:

from PIL import Image
im = Image.open('image.jpg')
img = np.asarray(im)

По нашим замерам такая загрузка (в среднем) на тестовом наборе изображений показывает 59ms/фото. Получается в планируемых 100 ms большая часть времени будет потрачена на загрузку изображения в память приложения. Есть ли что-то получше? Ну конечно! OpenCV однозначно быстрее, в нашем тесте он выдает 50ms/фото, но суровые датасаентисты предпочитают TurboJPEG, в нашем случае средняя скорость загрузки изменилась до 23ms/фото (ускорение в 2.5 раза)

from turbojpeg import TurboJPEG
jpeg = TurboJPEG()
with open('image.jpg', 'rb') as in_file:
    img = jpeg.decode(in_file.read())

Ускоряем ресайзинг фото

Ну ресайзинг же точно не может тормозить, эти алгоритмы, наверное, писала еще Ада Лавлейс. Дело в том, что число анализируемых точек большое и ресайзинг может выполняться за время, сравнимое с инференсом. Не поверите, в некоторых случаях загрузка с ресайзингом осуществяется даже быстрее чем просто загрузка этого же фото (например, если надо грузить большое фото с уменьшением в 2, 4 или 8 раз).

from turbojpeg import TurboJPEG
jpeg = TurboJPEG()
with open('image.jpg', 'rb') as in_file:
    img = jpeg.decode(in_file.read())
    # Only for scaling_factor (13, 8), (7, 4), (3, 8), (1, 2), (2, 1), (15, 8), (3, 4), (5, 8), (5, 4), (1, 1), (1, 8), (1, 4), (9, 8), (3, 2), (7, 8), (11, 8)
    img = jpeg.decode(in_file.read(), scaling_factor=(1, 2))

Но это частный случай для наших тестовых данных (да и уверен, что для ваших) наилучший результат дает комбинация в связке TurboJPEG+OpenCV.

from turbojpeg import TurboJPEG
import cv2
jpeg = TurboJPEG()
with open('image.jpg', 'rb') as in_file:
    img = jpeg.decode(in_file.read())
    (width, height) = (img.shape[1] // 2, img.shape[0] // 2)
    img = cv2.resize(img, [width, height], interpolation = cv2.INTER_AREA)

Результаты замеров на наших данных

Как видно, Pillow проигрывает TurboJPEG+OpenCV уже в 3.5 раза.
UPD: В коментах excentro спрашивал об ускоренной версии Pillow — Pillow-SIMD, я добавил прогон тестов и с этой библиотекой. Она быстее чем OpenCV, но в 1.5 раза медленнее чем TurboJPEG.

Ускоряем поисковую модель

Итак, в предыдущих статьях (Часть 1, Часть 2), я уже писал как изначально предполагалось находить зону с номером: ищем бинарную маску, которая обрамляет контур номерного знака, потом с помощью инструментария OpenCV интерполируем, выравниваем и получаем 4 точки, которые описывают четырехугольник с номером.

В первой версии библиотеки мы попробовали несколько моделей, которые решают задачу Instance Segmentation (нахождение маску(и)), наиболее точный результат показала сеть Mask R-CNN.
Работает это приблизительно так:

Mask RCNN Instance Segmentation Example

Мы взяли реализацию Mask R-CNN на Tensorflow v1, размер картинки для детекции маски, которая поступала на вход сети, 1000×1000. Это работало, но общая средняя скорость детекции оставляла желать лучшего, в среднем 840ms/фото.

Так уж сложилось исторически, что основные успехи в ускорении движка у нас проходили небольшими иттерациями, которые будем называть «этапы».

Оптимизация: Этап 1

Первая мысль по оптимизации — уменьшить размер изображения, который поступает на вход сети, мы пожертвуем детекцией номеров на заднем плане, что для наших данных не принципиально, так как пользователи сайта выкладывают фото авто, как правило, на переднем плане. Экспериментально остановились на размере 800×800.

В первых версиях Nomeroff Net фоновые номера также распознавались

Оптимизация: Этап 2

Mask R-CNN это не самая быстрая реализация для задачи Instance segmentation, кроме того, она написана на устаревшем tensorflow v1, поэтому мы начали искать альтернативу, остановились на малоизвестной “CenterMask 2” на PyTorch, с размером изображения 800×800. Мы ускорились приблизительно в 3 раза до 230ms/фото, это был определенно прорыв! Уменьшилось не только время детекции номера, но и потребление видеопамяти.

Оптимизация: Этап 3

На момент перехода на CenterMask 2 у проприетарных решений от конкурентов средняя скорость детекции была около 100-200ms/фото и это означало, что наше решение все еще было достаточно “жирное”. Снижая размер изображения до 640×640, начинало страдать качество нахождения маски. Cмена бэкенда, эксперименты с другими фреймворками для нахождения маски существенно ситуацию не улучшили. Приложение оставалось жирным и неповоротливым, надо было что-то менять в архитектуре.
Рецепт в такой ситуации простой: берем самые бредовые идеи и не стесняясь пробуем. В конце то концов, отрицательный результат — тоже результат! Одной из таких идей был отказ от поиска маски в пользу нахождения bounding box-а (рамки) с номерным знаком, эта операция проходит в разы быстрее и имеет множество шикарных реализаций.

Остановились на YOLOv5, натренированная модель находит bounding box довольно быстро, осталось научиться внутри рамки правильно оценивать как расположен текст, чтоб его нормализовать и передать OCR-ке. Для этого был использован фреймворк CRAFT с небольшими доработками системы интерпретации результатов модели.

Также оказалось, что для нахождения bounding box-а можно было еще уменьшить размер изображения до 640×640 без заметной потери качества.
Эта идея, в конечном итоге, дала самый большой прирост производительности и мы вышли на среднее время < 100ms/фото.

Yolo Bounding Box numberplate detection + CRAFT

OCR-модели на Nomeroff Net

Когда изображение с номерным знаком найдено и нормализовано, нам нужно его “прочитать”. В самой первой версии мы с этим не заморачивались и задействовали tesseract. Tesseract на выходе давал достаточно посредственное качество, это как у окулиста: читаешь нижнюю строчку, вроде все буквы увидел, но по факту половину не угадал.

Задумали написать свое, на начальном этапе идею подсмотрели в статье на сайте hackernoon.com, с приблизительно такой архитектурой

Deep Learning OCR architecture with Keras and Supervisely
Первая реализация была tensorflow v1, потом tensorflow v2, сейчас все работает на pytorch. Модель работает быстро, речь идет о нескольких милисекундах, тут усилия были направлены на улучшение качества, которое для большинства моделей составляет 99%.

Классификатор номеров

Если номера могут быть нескольких типов (например, транзитные, exUSSR с кириллицей, европейские, …) то это влечет за собой использование разных OCR для разных типов номеров. Перед OCR-кой для таких случаев мы поставили классификатор, сейчас это небольшая сверточная сеть на архитектуре resnet18. В качестве ускорения пробовали простую кастомную сеть на 4 свертки, но она давала немного хуже точность 98,5% против 96,6%. В результате решили пожертвовать 2 милисекундами и выбрали resnet18. Но если у вас Jetson Nano или Raspberry Pi 4, то я бы задумался над таким вариантом.

Nomeroff-Net OCR Example

Прикручиваем TensorRT от NVIDIA

На разных этапах работы над Nomeroff Net мы пробовали задействовать TensorRT для оптимизации моделей. Например, инференс TensorRT-модели для YOLOv5 проходит быстрее и сама модель потребляет меньше видеопамяти, особенно это заметно на устройствах с небольшим числом CUDA-ядер (например на тестируемом Jetson Xavier прирост составил в среднем 10ms/фото). Были эксперименты и с конвертацией моделей в ONNX-формат, запуск через ONNX Runtime c бэкендом на TensorRT, но там прирост был малозаметен на наших моделях. Но попробовать под свои задачи однозначно рекомендуем.

Что еще бы помогло

Уверен, что есть еще несколько направлений, по которым можно продолжить оптимизацию. О некоторых хочу написать.

  • YOLOv5: Мы выбрали среднюю по производительности модель (YOLOv5s), в угоду качеству и точности. Но если говорить о запуске распознавания на компактных устройствах, то можно попробовать менее точную, но зато более производительную версию YOLOv5n
  • CRAFT: Это лучшее решение задачи line detection из тех, что мы протестировали, но не идеальное, как по качеству, так и по скорости. Возможно, попробуем перетренировать модель на ствоих данных.
  • Определенное ускорение можно получить, обрабатывая фото батчами по n-фото, такой сценарий возможен, например, в случае получение фото с нескольких источников. Это могут быть камеры видеонаблюдения, с которых данные получаем в режиме онлайн или когда на систему распознавания номеров идет большой поток фото, которые можно сгрупировать на батчи. Мы планируем имплементровать этот подход в новой версии 3.0

Каков результат

На момент написания статьи (текущая версия Nomeroff Net 2.5) среднее время детекции 1 фото на наших данных на железе Intel® Core(TM) i9-9900K CPU @ 3.60GHz + NVIDIA RTX 3090 51,4ms/фото, также неплохие результаты получили на Jetson Xavier 284,7ms/фото, с TensorRT для YOLOv5 272ms/фото.

Нам удалось ускориться больше, чем на порядок, обратите внимание, что самая продолжительная операция по времени (около 20ms или 40% всей детекции) — загрузка фото, без TuboJPEG эти показатели были бы значительно хуже!

На втором месте по продолжительности выполнения находится применение модели CRAFT (около 12ms), мы уверены, что эту часть можно также ускорить. И если у нас получится это сделать, обязательно добавлю сюда результаты.

Спасибо за внимание, буду рад ответить на вопросы в комментариях.

Полезные ссылки

  • Распознавание номеров. Практическое пособие. Часть 1
  • Распознавание номеров. Как мы получили 97% точности для Украинских номеров. Часть 2
  • Nomeroff Net на GitHub
  • Онлайн демка Nomeroff Net

  • Пребывать в шоке как пишется
  • Пребывать во время как пишется
  • Пребывать в санкт петербурге как пишется
  • Пребывать в эйфории как пишется
  • Пребывать в течении года как пишется