<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>B</title><generator>teletype.in</generator><description><![CDATA[B]]></description><image><url>https://img4.teletype.in/files/b0/0a/b00a94a9-dca2-4b1c-bf0e-36fef31db8c8.png</url><title>B</title><link>https://teletype.in/@break_the_pain</link></image><link>https://teletype.in/@break_the_pain?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/break_the_pain?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/break_the_pain?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Tue, 10 Nov 2026 06:25:12 GMT</pubDate><lastBuildDate>Tue, 10 Nov 2026 06:25:12 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@break_the_pain/de7Fp1I95v7</guid><link>https://teletype.in/@break_the_pain/de7Fp1I95v7?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain</link><comments>https://teletype.in/@break_the_pain/de7Fp1I95v7?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain#comments</comments><dc:creator>break_the_pain</dc:creator><title>Первая летопись: о крови, что открыла врата</title><pubDate>Wed, 21 Oct 2026 21:05:32 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/45/2a/452a3bb6-735f-457f-94b5-4c51ddb056ce.png"></media:content><description><![CDATA[<img src="https://img1.teletype.in/files/c5/59/c559388d-896b-433c-afae-07852d21cad3.jpeg"></img>То ли ком, то ли гной в горле все не дают освободиться от этих дум. Местами абсурдных от чего теряю целостность истории, что разворачивается где то там за пределами, а скорее за шторками на которых виднеется силуэт то ли тень, то ли что то проступающее. Проявить бы себя, да в чем - остается лишь гадать. Впрочем, чего гадать, проще просто начать уже эту историю. …Там был он, смотрящий в небо. В мыслях - власть и месть. Решил отречься от человеческой оболочки твердо, но безрассудно. Решил, что избран он и достоин места рядом с теми кому поклонялся. Но кем же надо быть и что же сделать, чтобы быть о себе такого мнения - его не заботило. Он словно родился и понял: я Бог. Раздумья о великом прервал хруст травы от шагов. Это была его правая...]]></description><content:encoded><![CDATA[
  <h2 id="SxBs">I. О четырех душах</h2>
  <p id="0HKA">То ли ком, то ли гной в горле все не дают освободиться от этих дум. Местами абсурдных от чего теряю целостность истории, что разворачивается где то там за пределами, а скорее за шторками на которых виднеется силуэт то ли тень, то ли что то проступающее. Проявить бы себя, да в чем - остается лишь гадать. Впрочем, чего гадать, проще просто начать уже эту историю.<br />…Там был он, смотрящий в небо. В мыслях - власть и месть. Решил отречься от человеческой оболочки твердо, но безрассудно. Решил, что избран он и достоин места рядом с теми кому поклонялся. Но кем же надо быть и что же сделать, чтобы быть о себе такого мнения - его не заботило. Он словно родился и понял: я Бог.<br />Раздумья о великом прервал хруст травы от шагов. Это была его правая рука - Агнес.<br />— Мы собрали добычу, можем выдвигаться, командир.<br />Перикл еще какое то время смотрел в небо словно оно вот вот с ним заговорит, но небо молчало.<br />— Сколько? — спросил он наконец.<br />Агнес показала четыре пальца, от чего Перикл раздраженно посмотрел в сторону, затем снова на Агнес и сквозь фальшивую улыбку произнес:<br />— Мало.<br />— Принято. Готовить солдат на следующий набег?<br />Перикл проигнорировал вопрос. Сделал пару шагов в сторону Агнес и они встретились глазами. В тот же миг Агнес перевела взгляд в сторону и Перикл прервал давление возникшее в воздухе:<br />— Кому ты молишься, Агнес?<br />— Богам, господин.<br />У Агнес перехватило дыхание от того что Перикл подошел уже к ней вплотную. А тот казалось был доволен таким простым и незамысловатым ответом.<br />— Если груз уже готов, собирай остальных и по коням. — вдруг заговорил Перикл уже на понятном для Агнес языке.<br />Агнес коротко кивнула и ушла. Перикл недолго простояв, также направился к повозке.<br />Четверо были завязаны и усижены в повозке. Женщина с виду лет 35, прижавшая колени, двое парней, почти мальчишек, и тощий старик, который смотрел в землю так словно уже лежал в ней. Перикл разглядывал их долго, как торговец проверяющий свой товар на целость. Затем отвернулся, направился к своему коню и подал знак Агнес что пора выдвигаться.<br />К вечеру отряд встал на привал у высохшего русла. Перикл, как всегда, сидел поодаль, спиной к огню и лицом к небу.<br />— Третий набег, а он опять недоволен — Дамон швырнул в костер ветку. — Чего он добивается?<br />— Не нашего, и тем более не твоего скудного ума дела, Дамон — отозвался Леонт, в своей манере и суровым выражением лица. <br />— Еще как моего. Пленных не продаем. Добро не берем. Кормим их, будто на убой...<br />— Может и на убой — тут же перебил Леонт, — приказ есть приказ.<br />Повисла тишина. Леонта знали как самого старого члена банды. Он опустил взгляд и сказал:<br />— Но все же что то в нем изменилось. Молчаливый стал наш командир. Меня это настораживает.<br />— А ты его другим знал?<br />— Знал — ответил Леонт и покинул круг у костра.<br />Голоса у костра стихли и только треск огня еще долетал до Перикла. В тот момент его ничего не волновало и он сидел почти неподвижно. Светлые волосы спадали на плечи, лицо было спокойным и чистым, без единого следа прожитых лет и пролитой крови. Высокие скулы, прямой нос. Те, кто видел его впервые, замирали: так мог бы выглядеть ангел, спустившийся на землю, никак не бывший главнокомандующий королевской гвардии. Выдавали его лишь глаза. Почти прозрачные, светлые, пустые повидавшие битву жизни и смерти, они смотрели куда то сквозь людей, словно те были лишь тенью на пути к чему то большему. <br />Он смотрел в небо. Звезды висели низко и холодно. Перикл обратил внимание на звезду которая словно дрогнула. Будто кто то там наверху, наконец обернулся. Но небо все также оставалось безмолвным.</p>
  <h2 id="rLVF">II. О первой молитве</h2>
  <p id="Rs6x">Тишина. Перикл смотрел как люди бегут навстречу друг к другу, как раскрываются их рты стягивая кожу, как падает конь разбрызгивая грязь и слякоть. Он опустил взгляд на свои руки. Они держали меч так крепко, что руки было не разжать. Он замер и не мог пошевелиться от чего поступал страх безысходности. На груди тускло блестел герб королевской гвардии. Тот самый, который он когда то срезал с плаща и бросил в огонь. Доспехи были крупные, но их веса он не ощущал. До сих пор не понимая как он здесь оказался, к нему начал возвращаться слух. В тот момент, он снова захотел ничего не слышать, так как уши зазвенели чрезвычайно острым визгом словно иглой дырявили его черепную коробку. Позже визг превратился в лязг, а в нем начали слышаться что то узнаваемое. Это были крики, гул, топат людей тяжело бегущих по ряхлой земле.<br />— Командир! Левый фланг! — вдруг послышался знакомый голос. Такой родной что казалось это был его внутренний голос который пытается вернуть Перикла к жизни. Он снова почувствовал власть над своим телом. Повернув голову налево он увидел всадника свирепо мчущего на него с булавой в руках. Словно внутренний толчок, по мышечной памяти Перикл отскочил назад, отвел свой меч направляя острием в сторону врага и совершил взмах прорубая ногу всадника по колено. Тот проскакав еще несколько метров начал падать с седла истошно крича что есть мочи. Крик прервал топорик прилетевший ровно в висок откуда то с боку. Перикл знал только одного воина с метким глазом, когда дело доходит до метании топоров. Из-за бегущих в бой мужей показывается Леонт.<br />— Опять мечтаешь посреди боя, а? — с ухмылкой сказал Леонт. В глазах радости не было, скорее неловкое волнение. — Соберись командир, я тебе не нянька.<br />— Леонт? — Перикл пристально взглянул на него. На его еще не выпавшие густые черные волосы. — Как ты...<br />— Они наступают! — крикнул Леонт, вытащил свой топор из черепушки всадника и ринулся в гущу битвы.<br />Перикл бросился следом. Он бежал расталкивая чужие плечи, пока Леонт все глубже терялся в поле битвы. Эти черные громоздкие фигуры будто специально закрывали обзор не давая Периклу сделать и шагу. Он уперся ладонью в очередную спину чтобы оттолкнуть и рука вошла в нее как в нагретый воск. От такого, Перикл потерял равновесие и чуть не упал, пока посмотрев вниз не обнаружил что земля теряет свою форму. Она словно плавится и всасывает в себя все живое что ступает по ней. Идти было все тяжелее. Чем больше оказывалось сопротивление, тем глубже всасывала эта вязкая смола. Перикл оказался в ней уже по пояс. Впереди, в десяти шагах, он увидел Леонта. Тот тоже погряз в земле, но не придавая этому никакого внимания все еще замахивался топором на кого то, кого Перикл не видел.<br />— Леонт! Что происходит?!<br />Леонт обернулся. Лицо его оплывало до неузнаваемости, медленно как свеча, но то что осталось от губ еще шевелилось:<br />— Ком-м-андил... Йевый... фоанг...<br />Перикл рванулся к нему и смола поднялась до груди и сковала его движения. Он потянул руку в сторону Леонта, но от него уже не осталось ничего что напоминало бы живого человека. Леонт почернел и слился с землей.<br />Перикл хотел крикнуть, но горло сжало. После еще некоторых усилий сделать хоть что то и сдвинутся с мертвой точки, он наконец сдался.<br />&quot;Пусть заберет&quot;<br />Он поднял глаза к небу. Он так был занят войной что и не заметил какое оно прекрасное. Ему осталось лишь лицезреть его величие.<br />Наконец земля сомкнулась над головой. Снова тишина.<br />— Командир — прозвучал хриплый усталый голос. — Командир, уже светает.<br />Перикл открыл глаза. Над ним склонился Леонт. Седой, с глубокими морщинами у глаз, но выражение его лица оставалось для Перикла все таким же светлым, каким бы Леонт не был строгим стариком на самом деле. Рука его лежала на плече Перикла и тот не заметил что уже слишком долго на него смотрит. От этого не по себе стало и самому Леонту:<br />— Ты чего? — нахмурился Леонт.<br />Периклу хватило лишь хорошенько моргнуть чтобы вернутся в реальность. Он отвел взгляд и сказал:<br />— Ничего. Собираемся.</p>
  <h2 id="dSap">III. О битве в лощине</h2>
  <p id="Omcs">Рассветало. Солнца еще не было видно на горизонте, но светлое небо уже выглядело прекрасно. Приятный утренний бриз не помешал Дамону уже трижды пожаловаться с тех пор как проснулся:<br /> — Сухари — он разглядывал свой паек так будто тот оскорбил его мать. — Опять гребаные сухари. Да какого хрена?! Почему мы так дерьмого питаемся?! — Он зыркнул в сторону крестьян в повозке, — мы их спасаем, а жрем хуже чем они.<br /> — Не хочешь есть, отдай тому кто хочет — отозвалась Агнес, затягивая седло. — Принесешь кому то радость. А заткнешься – двойную.<br /> Кай хихикнул и тут же осекся, поймав на себе взгляд Дамона.<br /> — Смешно тебе, малой? — спросил Дамон со злобным оскалом.<br /> — Нет — быстро ответил Кай.<br /> — Да ладно тебе, Дамон. Смешно же. — с улыбкой включился в круг Леонт. Дамон лишь отвернулся в сторону и что то неразборчиво пробормотал сквозь губы.<br /> Перикл вышел из-за деревьев. Разговоры чуть стихли. Он, не обращая ни на кого внимания, вскочил на седло. Отряд потянулся следом.<br /> Спустя несколько часов пути, Кай подъехал к Леонту поближе.<br /> — А куда мы теперь?<br /> — Вперед.<br /> — Я серьезно. Откуда командир всегда знает где пройдет обоз? За то время пока я в отряде, мы ни разу не промахнулись.<br /> Казалось что Леонт слегка растерялся, но было очевидно что он знал ответ.<br /> — Потому что он сам эти дороги и чертил. Это было давно, но маршруты похоже с тех пор не менялись.<br /> — Погоди, что значит сам чертил дорого?<br /> — Ну — Леонт помолчал. — Скажем так, десятину раньше сопровождали только гвардейцы.<br /> Вдруг в голову Кая прилетел почерневший кусочек сухаря. Он растерянно оглянулся. Позади ехал Дамон.<br /> — Не мучай старика, малой. Хочешь знать про десятину – спроси Агнес. Она тебе так расскажет, что неделю спать не сможешь — засмеялся Дамон, но в глазах читалось что он сам пережил последнее.<br /> — А ты?<br /> — А мне за болтовню не платят. — Следующее Дамон снова произнес неразборчиво сквозь губы смотря куда то в сторону — Мне и за набеги не платят. Благородное, чтоб его, дело. Гребаное благородство.<br /> Перикл поднял руку и отряд остановился.<br /> Дорого впереди уходила вниз в лощину зажатую между двух поросших лесом склонов. Перикл какое то время всматривался в даль, потом указал пальцем на север. Там, у самого края видимости, заметно поднималась пыль.<br /> — Обоз впереди — сказала Агнес.<br /> — Рановато — подметил Перикл.<br /> — Неужели из за нас?<br /> — Не знаю, но до них рано или поздно должны были дойти сведения о нашем отряде. Осторожность не помешает.<br /> — Принято — ответила она и повернулась к отряду. — Слушайте. Ждем их в лощине. Леонт – на правый склон. Дамон, ты со своими – на левый. Я выйду на обгон и попробую отвлечь. Помните, первым бьем лошадей, жреца не трогать.<br /> — Да их же горстка. Давайте ударим в лоб и покончим со всем разом — проворчал Дамон.<br /> — Приказы не обсуждаются, Дамон.<br /> Кай был не самым опытным бойцом, да и в отряде он всего пару месяцев. Если бы Леонт не разглядел в нем что то и не взял под свое крыло, тот так и остался бы голодать в отшибе королевства Родопа. Нервно он теребил поводья. Леонт заметил это, подъехал ближе и протянул один из своих топоров.<br /> — Держи. Попробуй.<br /> — Я не...<br /> — У тебя получится — Леонт уже разворачивал коня к склону.<br /> Перикл же в это время не отводил взгляд от пыли у горизонта и Агнес стоявшая ближе всех заметила, как его дрогнули его губы. Казалось он что то считал.<br /> Отряд рассыпался по склонам. Агнес задержалась у своего коня, доставая из седельной сумки свернутый серый плащ с вышитым на вороте знаком храма. Ткань выцвела но держалась крепко.<br /> Перикл подошел и протянул ей тяжелое кольцо-печатку потемневший от времени. Агнес уже видела его раньше но в руки брала впервые.<br /> — Тот свиток — сказал он. — Печать поставишь при них. Должно сработать.<br /> Она кивнула и надела кольцо на палец хотя оно было ей велико.<br /> Ждать пришлось недолго. Сначала послышался скрип колес, потом фырканье лошадей. Обоз медленно втягивался в лощину и показались четыре крытые повозки. Впереди всадник в гвардейском плаще, а рядом жрец в белом. Охраны было меньше чем обычно. Агнес облегченно вздохнула.<br /> С правого склона Кай смотрел вниз сжимая обретенный топор.<br /> — Мало их — шепнул он.<br /> Леонт ничего не ответил, но смотрел на повозки с подозрением.<br /> Агнес накинула капюшон и тронула коня.<br /> Уже через некоторое время она начала подъезжать к обозу в лоб. Никто сначала не обратил на нее внимание, но затем Агнес подъехав вплотную обратилась к ведущим ее мужчинам.<br /> — Да хранит вас свет озаренный Богами — Агнес склонила голову приложив руку к груди. — Я от настоятеля. Срочное веление.<br /> Гвардеец придержал коня. Жрец же рядом с ним даже не повернул голову, лишь глазами прошелся по ее плащу, по вышитому знаку и снова уставился вперед.<br /> — Говори — повелительным тоном бросил гвардеец.<br /> — На старой дороге видели разбойников. Настоятель велит свернуть на восточный тракт и идти через Белые броды.<br /> Она развернула свиток, достала из за пояса воск и при них прижала к нему кольцо. Агнес делала это уверенно так как не раз практиковалась и видела как это делается что руки помнили сами. Гвардеец взял свиток, но вместо того чтоб прочесть его, он всматривался в печать.<br /> — Белые броды? — повторил он. — Мне нравится эта дорога, но не слишком ли длиннный путь?<br /> — Я лишь гонец, господин.<br /> — Кстати красивая печать — он поднял на нее глаза. — Правда старая. Я таких не видел с тех пор как был мальчишкой.<br /> У Агнес медленно сжалось в груди. Она сдержала внезапную дрожь по телу.<br /> — Этой печатью не пользовались лет двенадцать — продолжил он сворачивая свиток. — Как раз с тех пор как ее владелец решил что он выше короля.<br /> Жрец заулыбался. Гвардеец поднял руку и холстина слетела с повозок разом, а под ней вместо ожидаемых крестьян засверкали шлемы.<br /> Агнес тут же потянула за поводья коня отклонившись назад и тут же свистнула первая стрела у самого уха. Не успела она сделать вдох как вторая влетела прямо в шею коня. Животное вскинулось захрипело. Агнес едва успела выдернуть ноги из стремян прежде чем он рухнул на бок подняв облако пыли.<br /> Агнес откатилась в сторону на четвереньках сдирая ладони. Кольцо слетело и звякнуло где то под копытами. Гвардейцы уже прыгали с повозок вынимая мечи.<br /> — Живой брать! — крикнул сидящий на коне командир. — Она приведет нас к главной цели.<br /> Агнес вытащила припрятанный кинжал и пред ней привстали шестеро.<br /> На правом склоне Леонт понял все на мгновение раньше чем увидел из за осторожного шороха в кустах.<br /> — Ложись!<br /> Он толкнул Кая и стрела ударила в ствол там, где стояла его голова. Из зарослей поднялись двое: один с луком и другой с коротким мечом. Тот что с мечом был уже рядом. Леонт ловким движением кинул мимо него топор и пробил лоб лучнику. Когда он потянулся за вторым топором, гвардеец налетел на него, сбил с ног и они покатились вниз по склону ломая кусты.<br /> У Кая перехватило дыхание от осознание того что он был в шаге от смерти. Руки тряслись, а топор в ладони казалось потяжелел. Внизу гвардеец уже сидел на старике. Они боролись, но преимущество было не на стороне Леонта. Каю пришло осознание что если он ничего не предпримет то лишится самого дорого ему человека. Он вскочил, занес топор над головой — &quot;у тебя получится&quot; — и бросил его во врага. Топор сдела пару оборотов в воздухе и с глухим стуком воткнулся врагу между лопаток. Тот выгнулся от боли всего на мгновение, но этого было достаточно чтоб Леонт перехватил меч с рук гвардейуа и перерезал ему горло. Кровь хлестнула в Леонта. Тот лишь толкнул покойника в сторону и стал тяжело дышать. Вытерев кровь с лица, он посмотрел на Кая с гордостью.<br /> — Ну — прохрипел он. — Говорил же.<br /> Где то внизу со стороны дороги раздался рев. Это был Дамон. Он несся вниз по склону неаккуратно перебирая ноги. За ним бежали его люди пытаясь угнаться.<br /> — К черту, к черту, к черту! Агнес, держись, мать твою!<br /> Он вылетел на дорогу и с ноги влетел в первого гвардейца, который тут же отлетел рухнув на бок. Он вытащил из ножен два изогнутых клинка, всадил в грудь второму, уклонился от выпада и отсек голову третьему. От Агнес его отделяли еще несколько врагов, но гвардейцы перестроились. Слаженно, щит к щиту они отрезали Дамона от своих. Дамон был прижат спиной к повозке прежде чем он успел понять что происходит.<br /> — Давайте ублюдки — закричал Дамон, отступая, пока не уперся к повозке за спиной. — Налетай, я вас всех тут и положу!<br /> В ответ гвардейцы лишь шагнули ближе. Но своим действием он отвлек внимание тех нескольких что уже нависли над Агнес. Воспользовавшись ситуацией она вскочила с места и попыталась всадить кинжал в шею ближе стоящего гвардейца, но тот отмахнулся и попытался ударить в ответ. Агнес в последнюю секунду отскочила, но острие меча достигло своей цели и оставила глубокий порез. Агнес упала на колено, зажимая плечо. Горячая кровь текла сквозь пальцы. Добежавшие люди Дамона встали в ступор не зная как поступить. Оборона гвардейцев была подготовленная. Тут их командир злобно рассмеялся.<br /> — Шайка бродячих псов, вы сами не ведаете что творите. Неужели вы решили что справитесь с нами? Скажите мне где он и может быть я вас пощажу.<br /> — Отсоси упырь! — жестко ответил Дамон.<br /> — Ты и понятия не имеешь кого прикрываешь! Где Перикл?!<br /> — Трахает твою жирную мамашу.<br /> — Ах ты урод — в ярости главный гвардеец подал знак лучнику и тот незамедлительно всадил стрелу ему в ногу.<br /> Дамон громко заревел от боли. Его люди казалось было уже чуть не ринулись к нему на помощь но тут же оцепенели от безысходности. Он уперся об повозку, но не устояв, рухнул на землю.<br /> — Ладно, спрошу еще раз. Где Перикл?<br /> — Здесь.<br /> Командир резко обернулся. Лучник, выпустивший стрелу, стоял с открытым ртом. Из его горла торчало лезвие. Потом лезвие исчезло и лучник повалился на землю. За ним стоял Перикл.<br /> Свободные гвардейцы тут же бросаются на него. Первый замахнулся сверху, но Перикл не стал парировать и шагнул прямо внутрь удара чтобы вонзить меч в основание поднятой руки. Гвардеец пал на земли корчась от боли. Второй выставил щит. Перикл совершил боковой взмах одной рукой, но тот был отвлекающий от спрятанного кинжала в другой руке, которая вонзилась в висок. В глазах гвардейца все произошло настолько быстро что если бы он ожил то не смог бы объяснить причину своей смерти. Кинжал так и остался воткнутым в голову неподвижно лежащего человека с выпученными глазами. Приближался третий гвардеец, но действовал он не столь решительно после увиденного.<br /> Строй у повозки дрогнул. Гвардейцы, которые держали людей Дамона, начали начали оборачиваться на шум за спиной и в этот момент люди Дамона будто очнувшись, с криками бросились вперед.<br /> — Взять ее! — послышалась команда от командира.<br /> Ближайший к Агнес гвардеец оттянул ее за волосы, вздернул с колен и прижал лезвие к горлу. Она была обессилена и не могла сопротивляться.<br /> — Еще шаг — сказал командир — и она умрет.<br /> Перикл остановился и поле боя замерло в ожидании следующих указании своих командиров. Все смотрели на них двоих.<br /> Командир гвардейцев тронул коня и подъехал ближе.<br /> — Двенадцать лет... Двенадцать лет тебя искали по всей Родопе. А ты, как оказалось, играешь в спасителя. Собрал себе команду отбросов и отбиваешь десятину. Они хоть знают сколько обозов ты сам довез до храма? Сколько душ ты лично передал жрецам, когда носил этот герб? — по выражениям лиц команды было заметно что для них это была новость. — Сотни! Сотни, Перикл. Чистые души вознесенные к престолу. Каждая из них сияет над Родопой, хранит наши дома, наши семьи и нашего короля! Но ты сбился с пути, Перикл. Ты предал Богов и решил что можешь отобрать то что принадлежит им? — он уже почти кричал демонстрируя свой уровень фанатизма. — Боги видят тебя, предатель. Боги все видят. Они видят каждую душу. И когда ты сгоришь, а ты сгоришь, а я буду стоять, а я буду стоять там и молиться, молиться чтобы огонь горел вечность, молиться чтобы огонь горел как можно жарче!<br /> — Довольно, Мирон<br /> Жрец сказал это в пол голоса но командир осекся. Он впервые за все время повернул голову и посмотрел на Перикла.<br /> — Боги на тебя не гневаются. Ты хорошо отслужил в свое время. Да и разве можно украсть у того, кто сам тебе отдает?<br /> То ли от облегчения то ли от абсурдности происходящего, Перикл чуть улыбнулся.<br /> — Сколько тебе еще осталось, Перикл?<br /> Улыбка сошла с его лица. Жрец смотрел настолько спокойно словно знал больше чем должен был. Озадаченность сменилось мыслью &quot;он что то знает? а если и так, то кто еще?&quot;. Вдруг из кустов прилетел топор. Гораздо прежде чем Перикл успел открыть рот. Это было точное попадание, с хрустом, в лоб гвардейца державшего Агнес. С обратной стороны черепа хлестнула кровь с содержимым внутри и окрасила дугу за его спиной в бордовый цвет. Тот даже не вскрикнул, лишь разинул пасть и упал наспину разбрызгав ошметки своего мозга.<br /> Агнес упала на колени тяжело дыша. Из кустов вышел Леонт опираясь на Кая. С кровью на лице, он уже прокрутил топор в воздухе, выбирая следующую цель.<br /> — А ну ка ребята, хорош отдыхать — сказав он, поднял топор над головой и на мгновение все замерло, но затем лощина снова взорвалась криками.<br /> Снова разгорелась жаркая битва. Люди Дамона бросились вперед, а гвардейцы которых застали врасплох, начали отступать. Мирон понимал что битва не на его стороне. Он в ужасе перегляделся и решил ускакать. Раненный Дамон заметил это первым и попытался встать чтобы помешать ему, но не смог пошевелить ногу. Он подозвал ближайшего солдата и приказывает ему, но Перикл остановил его жестом, говоря &quot;Пусть уходит&quot;. Дамона был недоволен таким приказом, но был бесполезным в бою поэтому солдат взял его под руку и оттащил в безопасное место. Кого не хотел отпускать Перикл, был жрец. Но тот отойдя в тыл врага, стоял сложив руки.<br /> Бой стих также внезапно как начался. Последние гвардейцы бежали в лес бросая щиты и мечи. Некоторые с тяжелыми ранениями скулили лежа на земле. Но жрец так и стоял у дальней повозки. Когда Перикл подошел, он сначала не обратил на него внимания. Он смотрел на повозку и проронил лишь:<br /> — Я благодарю Богов за нашу встречу.<br /> — Свяжите его — бросил Перикл.<br /> Жрец позволил себя связать. Без разговоров и сопротивления, словно все шло именно так как он задумал. А быть может он просто был достаточно благоразумен чтобы смирится со своей судьбой.<br /> Кай заглянул под холстину последней повозки.<br /> — Командир! Тут люди.<br /> Их было семеро. Самые обыкновенные люди в лохмотьях. Худые и перепуганные. Во рту у них был кляп, а руки и ноги связаны. Приманка, чтобы обоз выглядел настоящим, а может какой то нереализованный план Мирона. Были и женщины и старики. И мальчик лет двенадцати, который не плакал и не прятался, а сидел у самого края повозки и смотрел вверх. Казалось его абсолютно не волновало все вокруг.<br /> Перикл подошел к повозке. Люди шарахнулись от него и кто то заскулил сквозь кляп.<br /> — Развяжите их — сказал он. — Пора уходить отсюда.</p>
  <h2 id="puJq">IV. </h2>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@break_the_pain/qZtuBSye-iL</guid><link>https://teletype.in/@break_the_pain/qZtuBSye-iL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain</link><comments>https://teletype.in/@break_the_pain/qZtuBSye-iL?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain#comments</comments><dc:creator>break_the_pain</dc:creator><title>Одинаковые слова в одинаковых предложениях: Манифест (Часть 1)</title><pubDate>Thu, 29 Jan 2026 10:10:43 GMT</pubDate><description><![CDATA[<img src="https://img1.teletype.in/files/8f/58/8f581acd-cc91-4ce9-b0a6-a87cf9c7ad28.png"></img>29.01.2026]]></description><content:encoded><![CDATA[
  <p id="fEMN">Дата начала: 18.12.2025</p>
  <h2 id="74UI">Отсчёт начала</h2>
  <h3 id="ZoiI">Причины</h3>
  <p id="CZEB">Я устал от неопределенности, самоанализа и постоянных попыток найти себя. Постоянных попыток найти в себе то что я вижу в других людях. Я устал искать в себе проблемы и сравнивать их с чужими. Сравнивать себя со стадом людей. Я хочу снова вести их за собой. Вливать в их опустошенные сосуды жизни праведность и истину. Я больше не буду жить по чужим правилам. </p>
  <p id="eBaN">Я возвращаю себе право быть источником направления и этот манифест - точка начала нового порядка.</p>
  <h2 id="QOwM">Здесь и сейчас</h2>
  <h3 id="5XPp">Реальность</h3>
  <p id="taJx">Я часто противоречу самому себе и создаю парадоксы в своей же голове. Я манипулирую своими убеждениями так как мне выгодно. Я никогда не придерживаюсь одного определения. В чьих то глазах я могу быть ублюдком, а в чьих-то - человеком с добрым сердцем. Это не потому что я с кем то схожусь в характере, а с кем то нет. Не потому что мои ценности отличаются или совпадают. Это потому что я выбираю быть тем кем захочу. Все что меня волнует - это удовлетворения моего любопытства и выгоды которую потенциально я могу получить. Я признаю это без оправданий.</p>
  <p id="BqiY">Я избегаю идиотов и тупых людей. Я не боюсь заразится идиотизмом, у меня иммунитет. Я боюсь сожаления и потери контроля. Я запрещаю себе сближаться с тупыми людьми. Ими легко манипулировать, получить выгоду, заставить делать то что тебе нужно. У меня есть склонность легко впадать в крайности и я знаю что я легко перейду грани дозволенности. Поэтому я ограничиваю себя в коммуникации с тупыми людьми.</p>
  <p id="4yyF">Меня злит высокомерие. Меня злят насмешки, взгляды сверху вниз. Высокомерные люди как игрушки, вообразившие себя живыми. Они разговаривают, двигаются, издают самостоятельно звуки, делают вид будто сами выбирают свой путь, но забывают свое место в мире: они все лишь игрушки, они созданы чтобы ими играться, они созданы для чужих рук. </p>
  <p id="IlvN">Я теряю энергию когда у меня что то не получается. Я обретаю ее когда получается. Для меня успех - это не статус и не показатель, это чистое топливо.</p>
  <h2 id="NAvT">Ключевые ценности</h2>
  <h3 id="5HLa">У всего есть цена</h3>
  <p id="4hqU">Я выбираю рост, а не мгновенный результат. <br />Я выбираю путь, а не цель. </p>
  <p id="ph6H">Для меня важна осознанность и ясность ума, потому что без них, я не вижу куда я иду и зачем.</p>
  <p id="Fzde">Я уважаю любопытство - так я познаю мир и расширяю границы своего сознания.</p>
  <p id="ygVD">Я выбираю медленно и продуманно, чем быстро и импульсивно, потому что скорость без направления - это потеря контроля.</p>
  <h2 id="VJQa">Принципы принятия решений</h2>
  <p id="yjEA">Если нарушаются установленные нормы коммуникации установленные временем, происходит отклонение - это нарушение структуры, соответственно ошибка системы от которой, требует единственный &#x27;способ лечения&#x27; - это полное абстрагирование. </p>
  <p id="skxE">Если я не понимаю зачем я делаю что то, следует глубокий анализ в целях возвращения ясности и стремления к адекватности и рассудительности в каждой ситуации.</p>
  <p id="iQu5">Если решение затуманено эмоциями, следует принятие и осознание искренности и праведности принятого решения. Мое подсознание не имеет парадоксов и отклонении. Все что я делаю это целостность. Все отклонения - это сигнал, никак не поддаются истине.</p>
  <p id="KGyo">Если я ловлю себя на самообмане, я останавливаюсь и начинаю размышлять о истинной сути. Я не ищу в себе противоречивые высказывания, я ищу причину.</p>
  <p id="FZLZ">Если выбор подразумевает за собой сложности и ухудшения качества жизни взамен на рост, я делаю его.</p>
  <p id="Vjfp">Если я упораюсь в стену и перестал видеть путь вперед, я временно перезагружаюсь и переключаюсь на новое.</p>
  <p id="fs4x">Если жизнь начинает казаться слишком легкой и беспрепятственной, я настораживаюсь и проверяю, не остановился ли рост.</p>
  <p id="XxIk">Если мне действительно действительно интересно, я даю себе свободу и отпускаю затверделость моего поведения, сохраняя ясность и контроль.</p>
  <h2 id="OCgT">Границы</h2>
  <h3 id="i0kw">Все что недопустимо</h3>
  <p id="LxyA">Я не отвечаю агрессии агрессией.</p>
  <p id="u4I6">Я не проявляю яркие эмоции перед кем то.</p>
  <p id="8H2j">Я не злюсь на человека и не держу обиду долгое время, ведь всему есть причина и объяснение.</p>
  <p id="0W6X">Я не задаю много вопросов там где меня это не касается.</p>
  <h2 id="67br">Ответственность</h2>
  <h3 id="HdVX">Ответственность, как про отношение к себе</h3>
  <p id="6oHz">Обо мне говорят как о ответственном человеке. Но по правде говоря, в моей голове есть лишь горсть вещей за которую я беру осознанную ответственность, которая в свою очередь участвует в построении моей личности. Я отвечаю за процесс. Я отвечаю за принимаемые мной решения. Я отвечаю за настоящего, в буквальном смысле, себя. Я отвечаю за то что пишу сейчас. Я отвечаю за то что говорю, когда разговариваю. Я отвечаю в моменте, ни моментом ранее, ни моментом позднее. Ответственность это постоянно обновляемое состояние меня. </p>
  <h2 id="AxAX">Направление</h2>
  <h3 id="958c">То куда ведет меня мое Я</h3>
  <p id="qNG0">В моей жизни, направления менялись как компас в Бермудском треугольнике. Я ни раз в своей жизни, изменял маршруты и сворачивал хаотичными путями. Можно сказать, порой я взваливал ответственность за свою жизнь на плечи судьбе. Тем не менее, есть направления жизни, которые укрепились в моем сознании. Например, я всегда шел на встречу одиночеству, грусти и тоске. Но последнее время, во мне усиливается желание морально утопить себя, чтобы заново переродится. Сейчас я на пути к очищению, и боль это мое лекарство. Во мне исчезают эмпатия, сострадание, искренность, способность кого-то выслушать и принятие чужой уязвимости. Это безусловно хорошие качества, но пока они лишь преграда </p>
  <h2 id="52iz">Пред-конец </h2>
  <h3 id="5dF5">Есть что сказать напоследок</h3>
  <p id="HxEp">Я напишу вторую часть или исправлю нынешний вариант, как только в моей жизни случится кризис</p>
  <p id="SsGE">При обычном разговоре или взаимодействии с людьми, я никогда не буду оправдываться этим манифестом и не буду на него ссылаться</p>
  <p id="s4VU">Б.Б 29.01.2026</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@break_the_pain/cM1SFUIf4vH</guid><link>https://teletype.in/@break_the_pain/cM1SFUIf4vH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain</link><comments>https://teletype.in/@break_the_pain/cM1SFUIf4vH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain#comments</comments><dc:creator>break_the_pain</dc:creator><title>Data Science подход к прогнозированию обращений в контакт-центре</title><pubDate>Wed, 12 Nov 2025 12:04:44 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a9/88/a98850bf-a619-482e-95fb-6ce1a0cfc3a7.png"></media:content><description><![CDATA[<img src="https://img4.teletype.in/files/bf/3f/bf3f5956-0229-4b6e-8500-b55c90f24252.png"></img>Контакт-центр является ключевым звеном взаимодействия банка с клиентом. Через него проходят обращения напрямую влияющие на выполнение операционных KPI. Таких как уровень обслуживания (SLA), время ожидания, доля успешно обработанных с первого звонка (FCR - First Call Resolution), ну и общий клиентский опыт.]]></description><content:encoded><![CDATA[
  <h2 id="sbZM">Введение</h2>
  <p id="JBIK">Контакт-центр является ключевым звеном взаимодействия банка с клиентом. Через него проходят обращения напрямую влияющие на выполнение операционных KPI. Таких как уровень обслуживания (SLA), время ожидания, доля успешно обработанных с первого звонка (FCR - First Call Resolution), ну и общий клиентский опыт.</p>
  <p id="L6XV">Для контак-центра, а именно для входящей линии прогнозирование количества входящей линии важно. По нему можно планировать персонал: недооценка ведёт к росту времени ожидания и потери звонков. Но в то же время переоценка ведет к лишним тратам на оплату труда. Данная проблема является актуальной в X банке для которого я и написал модель прогнозирования(доля потерянных звонков находится в районе 25-35%). То есть задача прогнозирования является стратегической частью управления. </p>
  <p id="qrAf">Для прогноза в данной работе используется <strong>рекурентная нейронная сеть GRU (Gated Recurrent Unit). </strong></p>
  <blockquote id="2PSZ"><em>ChatGPT:</em><br />Главные преимущества <strong>GRU (Gated Recurrent Unit)</strong>:<br /><br />1. <strong>Улавливает долгосрочные зависимости</strong> во временных рядах, не теряя контекст при длинных последовательностях.<br /><br />2. <strong>Меньше параметров</strong>, чем у LSTM — обучается быстрее и требует меньше данных.<br /><br />3. <strong>Избегает проблемы затухающего градиента</strong>, что делает обучение стабильным.<br /><br />4. <strong>Хорошо работает на нерегулярных и шумных данных</strong>, типичных для реальных бизнес-процессов (например, звонков).<br /><br />В итоге GRU — это компромисс между скоростью и качеством LSTM: легче, быстрее, но всё ещё «понимает» время.</blockquote>
  <p id="uT0s">Задача направлена на построение нейросетевой модели, прогнозирующей количество входящих звонков в контакт-центр банка на горизонте 30 дней с шагом 30 минут.</p>
  <hr />
  <h2 id="pMa9">Описание данных</h2>
  <p id="AiNt">Для того чтобы собрать данные и в дальнейшем использовать их для анализа, для начала важно понять их природу. Звонки во входящую линию можно описать в виде воронки (см. Рис. 1)</p>
  <figure id="y8x6" class="m_column">
    <img src="https://img2.teletype.in/files/5f/8c/5f8c3196-fcc9-4755-8d47-d8d859247f33.png" width="728" />
    <figcaption>Рис. 1: Воронка входящей линии</figcaption>
  </figure>
  <p id="g2zt">После звонка в горячую линию, клиент попадает в IVR - Interactive Voice Response, и далее вниз по воронке. Заказчику нужен прогноз всех звонков поступающих в IVR.</p>
  <p id="vhdY">Уже для построения модели использовались исторические звонки агрегированные в статистику обращений клиентов. Каждый элемент временного ряда является 30 минутным интервалом и к нему соответствует количество звонков в этом интервале. </p>
  <p id="XxRY">Исходные данные содержат два признака:</p>
  <ul id="CxL7">
    <li id="BDI5"><strong>datetime</strong> - временная метка (дата и начала интервала);</li>
    <li id="jDYr"><strong>count</strong> - количество входящих вызов в данный интервал.</li>
  </ul>
  <p id="awnS">Период исторических данных: <strong>от 01.03.2025 до 10.11.2025</strong></p>
  <pre id="h62U">Пример:
 datetime           | count
 09.07.2002 02:30:00| 67
 11.09.2001 08:30:00| 11</pre>
  <hr />
  <h2 id="OF8m">Очистка и обработка данных</h2>
  <p id="HXpa">Это мой второй опыт построения временных прогнозов и вот основные шаги предобработки, к которым я пришел: </p>
  <ol id="PaaE">
    <li id="nsJi">Базовая очистка данных от пустых значении;</li>
    <li id="KwdA">Усреднение аномальных выбросов. Так как звонки явление нестабильное и аномальные выбросы могут оказать большое негативное влияние на обучение модели;</li>
    <li id="jxYr">Масштабирование (другими словами нормализация).</li>
  </ol>
  <h3 id="7axm">Первичная очистка</h3>
  <pre id="SgAL" data-lang="python">data = df_final.copy().sort_values(&#x27;datetime&#x27;)
data[&#x27;datetime&#x27;] = pd.to_datetime(data[&#x27;datetime&#x27;])
data = data.dropna(subset=[&#x27;datetime&#x27;, &#x27;count&#x27;]).reset_index(drop=True)
data = data.set_index(&#x27;datetime&#x27;).sort_index()</pre>
  <p id="gvRn"></p>
  <figure id="hwOh" class="m_column">
    <img src="https://img3.teletype.in/files/20/5b/205b2d9c-9b01-458f-b402-41bd02bb5c09.png" width="1908" />
    <figcaption>Рис. 2: Исходный временной ряд (посл 7 дней)</figcaption>
  </figure>
  <h3 id="u9Bh">Обнаружение и замена аномалий</h3>
  <p id="5syV">Для выявления выбросов использовался алгоритм <strong>Isolation Forest</strong>. Он помечает редкие значения (аномалии) как -1.</p>
  <p id="OHPT">Чистка аномалии значило простая замена на медианные значения неаномальных точек. </p>
  <pre id="XALO">iso = IsolationForest(contamination=0.001, random_state=42)
iso.fit(scaled)
anom_df[&#x27;anomaly&#x27;] = iso.predict(scaled)
median_value = anom_df.loc[anom_df[&#x27;anomaly&#x27;] == 1, &#x27;count&#x27;].median()
anom_df.loc[anom_df[&#x27;anomaly&#x27;] == -1, &#x27;count&#x27;] = median_value</pre>
  <figure id="5zdi" class="m_column">
    <img src="https://img2.teletype.in/files/51/2f/512f3fed-91da-4c42-aab4-09c3fe607fc6.png" width="1910" />
    <figcaption>Рис. 3: Выявление аномалии во временном ряде</figcaption>
  </figure>
  <h3 id="QB2f">Масштабирование</h3>
  <p id="g835">После очистки, значения нормализовались при помощи <strong>StandardScaler</strong>, чтобы упростить для модели, абсолютные величины. Он убирает смещение и выравнивает разброс данных. Простыми словами все значения <strong>count</strong> по типу 6, 12, 143..., он размещает в одинаковый диапазон от -1 до 1. Для каждого типа задачи, существуют свои алгоритмы. Например TFIDF Vectorizor (из прошлой статьи) тоже в какой то мере является одним из таких алгоритмов так как, он помещает каждое слово в диапазон от 0 до 1.</p>
  <figure id="AcUF" class="m_column">
    <img src="https://img3.teletype.in/files/e8/cc/e8cc1a25-5956-48bb-92f0-aeaab996ef0c.png" width="768" />
    <figcaption>Таблица 1: Нормализация при помощи StandardScaler</figcaption>
  </figure>
  <figure id="bazh" class="m_column">
    <img src="https://img3.teletype.in/files/a8/9a/a89ae918-f6a2-43d6-9cfd-5551b6f5c253.png" width="1904" />
    <figcaption>Рис. 4: Нормализация временного периода при помощи StandarScaler</figcaption>
  </figure>
  <hr />
  <h2 id="oN80">Инженерия признаков (Feature Engineering)</h2>
  <p id="ztcC">После предобработки, был выполнен этап создания признаков. В этом этапе, мы преобразуем простой ряд <strong>count</strong> в многомерный набор данных, отражающий временные, календарные и статистические закномерности звонков. Простыми словами: превращаем линейный количественный рад в пространство с полезными данными.</p>
  <h3 id="9KzC">Календарные признаки</h3>
  <p id="nF2u">Для каждого временного интервала были добавлены базовые календарные фичи:</p>
  <ul id="e2qR">
    <li id="cFvW">день недели,</li>
    <li id="g1pc">час,</li>
    <li id="FH2j">половина часа,</li>
    <li id="HRvf">признак выходного (weekend).</li>
  </ul>
  <p id="zXwJ">Дополнительно применено <strong>one-hot-кодирование</strong> для дней недели и 30 минутных временных слотов, чтобы нейронка не воспринимала их как непрерывные величины.</p>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="8cXf">Пояснение:<br /><strong>One-hot-кодирование</strong> - это способ превратить категории в набор чисел, понятных моделей. Недели и половина часа являются категориальными.</p>
    <p id="pu6z">Если просто дать модели данные типа Понедельник - 0; Вторник - 1;.. Воскресенье - 6, это не передаст &quot;категориальность&quot; дней недели. Другими словами, модель может решить что воскресенье больше чем понедельник. Поэтому мы создаем отдельный столбец для каждого дня недели:</p>
    <pre id="cw8n">weekday_0  weekday_1  ...  weekday_6
1           0              0     → Понедельник
0           1              0     → Вторник
...</pre>
    <p id="LpR0">Таким образом мы позволяем модели воспринимать каждый день недели как уникальный признак, каким он и является.</p>
  </section>
  <pre id="5pLw" data-lang="python">df = data.copy()
df[&#x27;weekday&#x27;] = df.index.weekday
weekday_dummies = pd.get_dummies(df[&#x27;weekday&#x27;], prefix=&#x27;weekday&#x27;).astype(int)
df = df.join(weekday_dummies)

df[&#x27;hour&#x27;] = df.index.hour
df[&#x27;half&#x27;] = (df.index.minute // 30).astype(int)
df[&#x27;is_weekend&#x27;] = (df[&#x27;weekday&#x27;] &gt;= 5).astype(int)</pre>
  <figure id="1lzA" class="m_column">
    <img src="https://img3.teletype.in/files/24/df/24df5b36-7e8d-4e83-b29e-261927d810de.png" width="1304" />
    <figcaption>Рис. 5: Среднее (mean) количество звонков по дням недели</figcaption>
  </figure>
  <h3 id="ujX4">Лаговые признаки</h3>
  <p id="bdZp">Чтобы модель могла учитывать предыдущие значения ряда, созданы <strong>лаги (Inq_1 - Inq_10)</strong> - значения количества звонков за 1-10 предыдущих интервалов. Это позволяет нейронке опираться на предыдущие значения и &quot;чувствовать&quot; тренд. </p>
  <pre id="3PpV" data-lang="python">for k in range(1, 11):
    df[f&#x27;Inq_{k}&#x27;] = df[&#x27;count&#x27;].shift(k)    </pre>
  <figure id="TnbR" class="m_column">
    <img src="https://img4.teletype.in/files/72/53/725376bb-46c6-488c-a4e5-cb598bd9a185.png" width="1332" />
    <figcaption>Рис. 6: Корреляция между лагами и текущим значением</figcaption>
  </figure>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="AlAM">Пояснение:</p>
    <p id="qhkE">Это тепловая карта корреляции между количеством звонков (count) и лаговыми признаками (Inq_1, Inq_2,.. Inq_10).</p>
    <p id="pne1">Каждая ячейка отражает, <strong>насколько сильно связаны</strong> между собой два признака.</p>
    <ul id="MS6b">
      <li id="9OzJ">Значение +1 - идеальная положительная связь (если один растет то другой тоже)</li>
      <li id="XB9x">Значение 0 - отсутствует корреляция</li>
      <li id="LpIL">Значение -1 - идеальная отрицательная связь (то есть когда один растет, другой падает)</li>
    </ul>
  </section>
  <h3 id="evvw">Скользящие статистики</h3>
  <p id="AeKL">Тут я думал назвать раздел просто статистические признаки, но вариант &quot;Скользящие статистики&quot; звучит гораздо корректнее и вот почему:</p>
  <p id="Nxsk">Для учёта кратковременных трендов, добавлены скользящие метрики:</p>
  <ul id="1AwG">
    <li id="zJIM">среднее значение за последние 7 интервалов Inq_mean_7;</li>
    <li id="tKvW">стандартное отклонение за 3 интервала Inq_std_3</li>
  </ul>
  <p id="sSZC">Каждый последующий интервал двигает окно интервалов вперед на одну и таким образом оба признака <em><strong>скользят</strong></em> по ряду.</p>
  <pre id="xjV3">df[&#x27;Inq_mean_7&#x27;] = df[&#x27;Inq_1&#x27;].rolling(window=7).mean()
df[&#x27;Inq_std_3&#x27;]  = df[&#x27;Inq_1&#x27;].rolling(window=3).std()</pre>
  <figure id="iWCk" class="m_column">
    <img src="https://img1.teletype.in/files/86/63/8663f0b1-ae05-4dfb-b003-c969e4c3c5ed.png" width="1910" />
    <figcaption>Рис. 7: Скользящее среднее и стандартное отклонение количества звонков (посл 7 дней)</figcaption>
  </figure>
  <h3 id="q9zc">Итог</h3>
  <p id="ZRNg">Все вышеописанные признаки включая <strong>count</strong>, войдут, а именно станут основой входных данных для обучения модели.</p>
  <hr />
  <h2 id="lTkv">Масштабирование и разделение данных</h2>
  <p id="5LaR">Этот этап состоит из двух шагов: нормализация значений и разделение временного ряда на обучающую и тестовую части.</p>
  <h3 id="nD2d">Масштабирование признаков</h3>
  <p id="DT8s">Различные созданные нами признаки имею слишком разные масштабы. Например часы варьируются от 0 до 23, в то время как дни недели бинарны. GRU-модель, а скорее любая машинная модель чувствительная к подобному. Она не сможет в полной мере измерить важность значении признаков по типу 0.23, -0.98 в то время как соседний признак измеряется в сотнях типа 231, 563.</p>
  <figure id="VJqv" class="m_column">
    <img src="https://img1.teletype.in/files/4a/09/4a095c80-a060-4067-912a-19c5c243502c.png" width="3000" />
    <figcaption>Рис. 8: Иллюстрация нормализации</figcaption>
  </figure>
  <p id="hZnt">Чтобы уровнять все признаки, все численные колонки (лаги, средние и тд) приводятся к диапазону <strong>от -1 до 1</strong> с помощью <strong>MinMaxScaler</strong>.</p>
  <pre id="G7jV" data-lang="python">from sklearn.preprocessing import MinMaxScaler

target_col = &#x27;count&#x27;
y = df[target_col].copy()
X = df.drop(columns=[target_col, &#x27;weekday&#x27;, &#x27;time_of_day&#x27;])

num_cols = [c for c in X.columns if c.startswith(&#x27;Inq_&#x27;) or c in [&#x27;hour&#x27;, &#x27;half&#x27;]]
scaler_x = MinMaxScaler()
X_scaled = X.copy()
X_scaled[num_cols] = scaler_x.fit_transform(X[num_cols])

scaler_y = MinMaxScaler()
y_scaled = scaler_y.fit_transform(y.values.reshape(-1, 1))</pre>
  <h3 id="cCVg">Разделение на обучающую и тестовую выборки</h3>
  <p id="4lQN">Так как это <strong>временной ряд</strong>, разделение выполняется не случайным образом, как это обычно происходит, а по времени, не теряя последовательность:</p>
  <ul id="r5pd">
    <li id="3u1l"><strong>обучающая часть</strong> - все данные до последних 30 дней;</li>
    <li id="aTZp"><strong>тестовая часть</strong> - последние 30 дней (1440 строк по 30 минутным интервалам)</li>
  </ul>
  <p id="eFaO">Это нужно чтоб модель училась на прошлом и проверялась на будущем.</p>
  <pre id="ZJxG" data-lang="python">test_len = 48 * 30  # 30 дней по 48 интервалов в каждом
X_train, X_test = X_scaled.iloc[:-test_len], X_scaled.iloc[-test_len:]
y_train, y_test = y_scaled[:-test_len], y_scaled[-test_len:]</pre>
  <h3 id="pUYQ">Подготовка формы</h3>
  <p id="hM9G">Ожидается вход трехмерных данных:</p>
  <p id="fUzO"><strong>[samples, time_steps, features]</strong></p>
  <p id="H4nu">где <strong>time_steps</strong> - количество временных шагов (1).</p>
  <pre id="3r0s" data-lang="python">X_train_np = X_train.to_numpy().reshape(-1, 1, X_train.shape[1])
X_test_np  = X_test.to_numpy().reshape(-1, 1, X_test.shape[1])</pre>
  <figure id="u7jY" class="m_column">
    <img src="https://img4.teletype.in/files/3c/4e/3c4ea811-6746-444c-9043-20eedb0b0890.png" width="1910" />
    <figcaption>Рис. 9: Разделение временного ряда на <em>train</em> и <em>test</em></figcaption>
  </figure>
  <h2 id="po3a">Архитектура модели GRU</h2>
  <p id="azj0">С подготовкой покончено, и мы можем переходить к построению нейросетевой модели.</p>
  <h3 id="NZvF">Почему именно GRU?</h3>
  <figure id="GAVc" class="m_column">
    <img src="https://img4.teletype.in/files/71/9c/719c976e-98c1-47df-bdeb-d5c0a465adf5.png" width="300" />
    <figcaption>Рис. 10: Грю из мультфильма <em>Гадкий Я</em></figcaption>
  </figure>
  <p id="gG6r">Она &quot;помнит&quot; недавние значения временного ряда и понимает, как текущие данные зависят от прошлых.</p>
  <blockquote id="yEgI">ChatGPT:<br />GRU эффективна, когда:<br />- нужно учитывать последовательность событий (время важно),<br />- данные содержат сезонные колебания,<br />- требуется баланс между скоростью и точностью.</blockquote>
  <h3 id="GibV">Структура сети</h3>
  <p id="yNHG">Слои модели:</p>
  <ol id="tDg5">
    <li id="PQRW"><strong>GRU(128)</strong> - первый слой с 128 нейронами, извлекает временные зависимости;</li>
    <li id="WNNM"><strong>Dropout(0.3)</strong> - случайно отключает 30% нейронов при обучении, чтобы избежать переобучения;</li>
    <li id="Afdc"><strong>GRU(64)</strong> - второй слой, обрабатывает более абстрактные паттерны;</li>
    <li id="ML0i"><strong>Dense(32, relu)</strong> - полносвязный слой, делает нелинейное преобразование признаков;</li>
    <li id="uBAP"><strong>Dense(1)</strong> - выходной слой, выдает одно число - то есть сам прогноз звонков.</li>
  </ol>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="I4GV">Настоятельно рекомендую посетить YouTube канал <a href="https://www.youtube.com/watch?v=9-Jl0dxWQs8&list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi" target="_blank">3Blue1Brown</a>, чтобы лучше понять как работают слои с красивой визуализацией и четким объяснением. </p>
  </section>
  <pre id="alp1" data-lang="python">from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import GRU, Dense, Dropout
from tensorflow.keras.callbacks import EarlyStopping

model = Sequential([
    GRU(128, return_sequences=True, input_shape=(1, X_train.shape[1])),
    Dropout(0.3),
    GRU(64, return_sequences=False),
    Dense(32, activation=&#x27;relu&#x27;),
    Dense(1)
])

model.compile(optimizer=&#x27;adam&#x27;, loss=&#x27;mse&#x27;, metrics=[&#x27;mae&#x27;])

es = EarlyStopping(monitor=&#x27;val_loss&#x27;, patience=20, restore_best_weights=True)

history = model.fit(
    X_train_np, y_train,
    validation_data=(X_test_np, y_test),
    epochs=120, batch_size=256,
    callbacks=[es], verbose=0
)</pre>
  <h3 id="JeNT">Обучение и контроль качества</h3>
  <p id="oXOt">В процессе обучения используется функция потерь <strong>MSE (Mean Square Error)</strong> - чем она меньше, тем ближе прогноз к реальности. Метрика <strong>MAE (Mean Absolute Error)</strong> используется для оценки средней ошибки.</p>
  <figure id="bnKY" class="m_column">
    <img src="https://img2.teletype.in/files/96/89/96894115-091e-453f-9900-f7238702f3fa.png" width="1528" />
    <figcaption>Рис. 11: Динамика ошибок обучения и валидации</figcaption>
  </figure>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="rhXO">Пояснение</p>
    <p id="4Cdq">То что вы видите на оси X, это количество эпох. То есть каждая попытка предсказания это одна итерация (эпоха).</p>
  </section>
  <hr />
  <h2 id="O4fp">Результат на тестовой сборке</h2>
  <p id="curb">После обучения тестируем модель на тестовом наборе данных. Это означает что модель попытается предсказать предыдущие 30 дней, а мы оценим эту модель.</p>
  <h3 id="0fOb">Расчет метрик качества</h3>
  <p id="3WgN">Для оценки точности использованы три классические метрики регрессии (вспоминаем что такое регрессия с прошлой статьи ;Р):</p>
  <blockquote id="AiAO">ChatGPT:<br />- <strong>MAE (Mean Absolute Error)</strong> — средняя ошибка в единицах измерения (например, “в среднем на 25 звонков в час ошибается”);<br />- <strong>RMSE (Root Mean Squared Error)</strong> — более чувствительна к крупным ошибкам;<br />- <strong>R² (коэффициент детерминации)</strong> — показывает, насколько хорошо модель объясняет изменения данных (1.0 — идеал, 0 — “не лучше случайного”).</blockquote>
  <pre id="d8II" data-lang="python">from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
import numpy as np

y_pred_scaled = model.predict(X_test_np, verbose=0)
y_pred = scaler_y.inverse_transform(y_pred_scaled)
y_test_real = scaler_y.inverse_transform(y_test)

mae  = mean_absolute_error(y_test_real, y_pred)
rmse = np.sqrt(mean_squared_error(y_test_real, y_pred))
r2   = r2_score(y_test_real, y_pred)

print(f&quot;Test MAE:  {mae:,.2f}&quot;)
print(f&quot;Test RMSE: {rmse:,.2f}&quot;)
print(f&quot;Test R²:   {r2:,.4f}&quot;)</pre>
  <figure id="plDg" class="m_column">
    <img src="https://img4.teletype.in/files/fb/4b/fb4b8786-6749-4940-9d06-a719df77bc4e.png" width="1910" />
    <figcaption>Рис. 12: Прогноза временного ряда на тестовом наборе</figcaption>
  </figure>
  <pre id="Odcu">Test MAE:  6.482
Test RMSE: 9.638
Test R²:   0.9463</pre>
  <h2 id="R0z9">Итеративный прогноз на 30 дней</h2>
  <p id="5a2M">После проверки на тестовой выборке модель можно использовать для <strong>реального прогнозирования будущих обращений</strong>.</p>
  <h3 id="CWkr">Принцип итеративного прогноза</h3>
  <ol id="NqWS">
    <li id="E1SQ">Берется последняя строка данных.</li>
    <li id="PiXL">Модель предсказывает, сколько звонков будет на следующий 30 минутный интервал.</li>
    <li id="qmQK">Это предсказанное значение добавляется в данные как &quot;новое прошлое&quot;.</li>
    <li id="OSjF">На основе обновленных лагов (Inq_1 - Inq-10) модель делает следующее предсказание.</li>
  </ol>
  <pre id="GgMy" data-lang="python">future_steps = 48 * 30  
# ШАГ 1
last_row = X.iloc[[-1]].copy()        # последняя строка немасштабированных признаков
last_feats_scaled = X_scaled.iloc[[-1]].copy()  # последняя строка в масштабе

forecast_vals = []
future_index = pd.date_range(df.index[-1] + pd.Timedelta(minutes=30),
                             periods=future_steps, freq=&#x27;30min&#x27;)

for t in range(future_steps):
# ШАГ 2
    x_in = last_feats_scaled.to_numpy().reshape(1, 1, -1)
    y_next_scaled = model.predict(x_in, verbose=0)[0][0]
    y_next = scaler_y.inverse_transform([[y_next_scaled]])[0][0]
# ШАГ 3
    forecast_vals.append(y_next)
# C ЭТОГО МОМЕНТА ОБНОВЛЯЕМ СКОЛЬЗЯЩИЕ ПРИЗНАКИ И ГОТОВИМ ДАННЫЕ К ПРЕДСКАЗАНИЮ СЛЕД ИНТЕРВАЛА
    next_time = future_index[t]
    nr = last_row.copy()
    nr.index = pd.DatetimeIndex([next_time])

    for k in range(10, 1, -1):
        nr[f&#x27;Inq_{k}&#x27;] = last_row[f&#x27;Inq_{k-1}&#x27;].values[0]
    nr[&#x27;Inq_1&#x27;] = y_next

    recent = [nr[f&#x27;Inq_{i}&#x27;].values[0] for i in range(1, 8)]
    nr[&#x27;Inq_mean_7&#x27;] = np.mean(recent)
    nr[&#x27;Inq_std_3&#x27;]  = np.std(recent[:3])

    nr[&#x27;hour&#x27;] = next_time.hour
    nr[&#x27;half&#x27;] = next_time.minute // 30
    nr[&#x27;is_weekend&#x27;] = int(next_time.weekday() &gt;= 5)

    nr_scaled = nr.copy()
    nr_scaled[num_cols] = scaler_x.transform(nr[num_cols])

    last_row = nr.copy()
    last_feats_scaled = nr_scaled.copy()

forecast_df = pd.DataFrame({&#x27;datetime&#x27;: future_index, &#x27;forecast&#x27;: forecast_vals})
</pre>
  <figure id="u8iz" class="m_column">
    <img src="https://img4.teletype.in/files/bf/de/bfde1b60-8866-40cb-9217-614be8511d51.png" width="1908" />
    <figcaption>Рис. 13: Временной ряд прогнозов на следующие 30 </figcaption>
  </figure>
  <h2 id="QM5n">Конец</h2>
  <p id="NZ1s">Спасибо все кто дочитал досюда эту статейку!</p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@break_the_pain/N5Qvny5XFEK</guid><link>https://teletype.in/@break_the_pain/N5Qvny5XFEK?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain</link><comments>https://teletype.in/@break_the_pain/N5Qvny5XFEK?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=break_the_pain#comments</comments><dc:creator>break_the_pain</dc:creator><title>SPAM or HAM</title><pubDate>Fri, 25 Oct 2024 00:11:57 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/a2/2a/a22ac443-a76e-4f11-8599-dda755e87628.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/de/3e/de3e702a-5da0-4308-8837-3256347d88db.webp"></img>В цифровое время очень важно фильтровать информацию которая приходит к вам на электронную почту или SMS. Часто бывает такое что почты пользователей превращаются в мусор из рекламы, скама и бесполезных объявлении. Эта статья о том как я написал модель на основе NLP* которая определяет является ли полученная смска спамом.

Я постараюсь быть краток и не выходить за грани инструментов и методов которые я буду использовать.]]></description><content:encoded><![CDATA[
  <h2 id="mQmm">Введение (Introduction)</h2>
  <p id="ZPT3">В цифровое время очень важно фильтровать информацию которая приходит к вам на электронную почту или SMS. Часто бывает такое что почты пользователей превращаются в мусор из рекламы, скама и бесполезных объявлении. Эта статья о том как я написал модель на основе NLP* которая определяет является ли полученная смска спамом.<br /><br />Я постараюсь быть краток и не выходить за грани инструментов и методов которые я буду использовать.</p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="GPKT">NLP (Natural Language Processing) — это обработка естественного языка, область искусственного интеллекта, которая занимается взаимодействием компьютеров и людей с использованием естественного языка. Цель NLP — позволить компьютерам понимать, интерпретировать и генерировать человеческий язык таким образом, чтобы они могли выполнять задачи, связанные с обработкой текстов или речи.</p>
  </section>
  <h2 id="7a8S">Постановка задачи</h2>
  <p id="lFZm">Любая машинная модель начинается с набора данных (или датасет). Очень важно выбрать корректный датасет, потому что именно она будет служить основой нашей модели. В моем случае, я выбрал датасет под названием &#x27;SMS Spam Collection Dataset&#x27;. Подробнее: <a href="https://www.kaggle.com/datasets/uciml/sms-spam-collection-dataset" target="_blank">https://www.kaggle.com/datasets/uciml/sms-spam-collection-dataset</a></p>
  <h3 id="YnPp">Описание данных:</h3>
  <p id="DcXs">Датасет представляет собой коллекцию SMS-сообщении которые классифицированы как ham и spam, то есть не спам и спам соответсвенно.<br />Датасет состоит из двух колон которые я позже переименую в:</p>
  <ul id="wC52">
    <li id="6qmV">Label (бинарная классификация* в виде spam и ham)</li>
    <li id="B8Jj">Text (SMS-сообщение)</li>
  </ul>
  <p id="kQ9r">Пример:<br />ham - &quot;Nah I don&#x27;t think he goes to usf, he lives around here though&quot;<br />spam - &quot;WINNER!! As a valued network customer you have been selected to receivea 900 prize reward!&quot;</p>
  <figure id="1e5a" class="m_original">
    <img src="https://img2.teletype.in/files/53/6c/536c39af-5059-4cc5-a8b9-5d3068ddb79b.png" width="754" />
    <figcaption>SMS Spam Collection Dataset</figcaption>
  </figure>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="oKBE">Бинарная классификация — это тип задачи в области машинного обучения и статистики, при которой требуется разделить данные на две категории или класса. В такой задаче модель обучается на наборе данных, где каждый пример помечен как принадлежащий к одному из двух классов.</p>
  </section>
  <h2 id="nytc">Гипотезы</h2>
  <h3 id="e5tS">Первая:</h3>
  <p id="uW55">Прежде чем мы начнем работать с текстом давайте подумаем над тем что может повлиять на само разделение между двумя видами текста. В глаза бросается длинна текста: spam сообщения гораздо длиннее чем ham.</p>
  <p id="VcLQ">Немного поанализировав, мы можем выяснить влияние длинны текста. Для этого создаем новую колону &#x27;len&#x27; чтоб разместить там длину текста на каждой строке:</p>
  <pre id="hR9y" data-lang="python">df[&#x27;len&#x27;] = df[&#x27;text&#x27;].apply(lambda x: len(x) - x.count(&#x27; &#x27;))</pre>
  <figure id="9lwc" class="m_original">
    <img src="https://img1.teletype.in/files/4d/26/4d26cc28-cfd5-40bd-b6e2-3ab5e436ba0f.png" width="754" />
  </figure>
  <p id="Pjfj">Теперь посмотрим как это выглядит на гистограмм.</p>
  <pre id="X5ou" data-lang="python">bins = np.linspace(0, 150, 50)
plt.hist(df[df[&#x27;label&#x27;] == &#x27;spam&#x27;][&#x27;len&#x27;], bins, alpha=0.5, label=&#x27;spam&#x27;)
plt.hist(df[df[&#x27;label&#x27;] == &#x27;ham&#x27;][&#x27;len&#x27;], bins, alpha=0.3, label=&#x27;ham&#x27;)
plt.legend()
plt.show()</pre>
  <figure id="qs8O" class="m_original">
    <img src="https://img4.teletype.in/files/f9/d2/f9d2a65d-2807-4b20-88f4-9ae15e7a4d7f.png" width="754" />
    <figcaption>X - длинна, Y - кол-во строк</figcaption>
  </figure>
  <p id="wZWd">По графику можно сказать что длинна сообщения действительно играет большую роль в определении типа текста. Так как spam текст сильно смещен направо в то время как ham налево. Вывод: есть необходимость использовать длину сообщения как один из параметров входных данных.</p>
  <h3 id="f5MB">Вторая:</h3>
  <p id="NXgi">Второе что так же может повлиять на определение это пунктуация. Как влияет частота пунктуации используемое в тексте. Проведем те же операции:</p>
  <h3 id="7h3D">def count_punct(text):<br />    count = sum([1 for char in text if char in string.punctuation])<br />    return round(count / (len(text) - text.count(&#x27; &#x27;)), 3) * 100<br /><br />df[&#x27;punct%&#x27;] = df[&#x27;text&#x27;].apply(lambda x: count_punct(x))<br /><br />bins = np.linspace(0, 40, 50)<br />plt.hist(df[df[&#x27;label&#x27;] == &#x27;spam&#x27;][&#x27;punct%&#x27;], bins, alpha=0.5, label=&#x27;spam&#x27;)<br />plt.hist(df[df[&#x27;label&#x27;] == &#x27;ham&#x27;][&#x27;punct%&#x27;], bins, alpha=0.3, label=&#x27;ham&#x27;)<br />plt.legend()<br />plt.show()</h3>
  <figure id="B8tM" class="m_original">
    <img src="https://img2.teletype.in/files/15/c7/15c709c2-452c-423c-a925-62296623e148.png" width="754" />
    <figcaption>X - % используемой пункиуации, Y - кол-во строк</figcaption>
  </figure>
  <p id="Osws">В этом случае все наоборот. Смещение примерно одинаковое, значит частота пунктуации не поможет определить является ли текст спамом. Более простыми словами: этот график говорит нам о том что и спам текст и не спам текст использует пунктуацию на одинаковой частоте.</p>
  <h2 id="NuC3">Предобработка данных (Data Preprocessing)</h2>
  <h3 id="fJSE">Шаг 1:</h3>
  <p id="oTFd">&quot;I am Batman.&quot; и &quot;I am batman!&quot; это не одно и то же предложение в глазах нашего алгоритма. Следовательно, для дальнейшей работы с текстом нам нужно полностью очистить ее от пунктуации и переписать все в нижний регистр. Тогда мы получаем простой, но понятный и удобный для работы &quot;i am batman&quot;.</p>
  <h3 id="TFG9">Шаг 2:</h3>
  <p id="pC0r">Не все слова несут за собой полезную информацию для обработки. Такие слова называются stopwords. Пример: I, me, am, are, hadn&#x27;t, where и тд. То есть из предложения &quot;i am batman&quot;, мы убираем слова I и am так как чтобы понять о чем предложение, нам буквально хватает одного слова &quot;batman&quot;. Давайте рассмотрим еще один пример: &quot;Can writing article be boring?&quot; превращается в &quot;writing article boring&quot;. Для человека, очевидно, полностью теряется контекст, но теперь наш алгоритм понимает что &quot;Can writing article be boring?&quot; имеет много общего с например &quot;Writing an article can be boring if the topic isn&#x27;t interesting&quot;. </p>
  <h3 id="f8VJ">Шаг 3:</h3>
  <p id="Mvie">Stemming и токенизация. Стемминг просто напросто упрощает слова. В частности он убирает окончания &quot;-ing&quot;, &quot;-ed&quot;, &quot;-tion&quot; и тд. Потому что нам при работе с текстом нет дела до грамматики и подобных окончании. Мы хотим чтобы слова такие как &quot;interesting&quot;, &quot;interested&quot; в работе были одним единым словом &quot;interest&quot;. А токенизация просто распределит предложение в массив из слов.</p>
  <p id="rmaq">Сложное и непонятное предложение как &quot;Writing an article can be boring if the topic isn&#x27;t interesting.&quot; превращается в удобное для работы массив из слов [&#x27;writ&#x27;, &#x27;articl&#x27;, &#x27;bor&#x27;, &#x27;topic&#x27;, &#x27;interest&#x27;]</p>
  <pre id="jHv5" data-lang="python">stopwords = nltk.corpus.stopwords.words(&#x27;english&#x27;)
ps = nltk.PorterStemmer()

def clean_text(text):
    text = &#x27;&#x27;.join([word.lower() for word in text if word not in string.punctuation])
    tokens = re.split(&#x27;\W+&#x27;, text)
    text = [ps.stem(word) for word in tokens if word not in stopwords]
    return text</pre>
  <h3 id="CZMM">Последний шаг:</h3>
  <p id="1SYv">Последний шаг представляет собой превращение слов в числа, но каким образом. Для нашей задачи я буду использовать векторизацию TFIDF. </p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="k5i9"><strong>TF-IDF</strong> (Term Frequency-Inverse Document Frequency) — это популярная методика для представления текста в виде числовых признаков в задачах обработки естественного языка (NLP). Она помогает оценить важность слов в документе с учетом того, насколько часто слово встречается в других документах.</p>
  </section>
  <p id="hb2s">Данная векторизация распределяет все слова в большую матрицу где колоны это все используемы слова, а строки это наши предложения. А значения это степень важности слова в предложении. </p>
  <pre id="zcM8">tfidf_vect = TfidfVectorizer(analyzer=clean_text)
X_tfidf = tfidf_vect.fit_transform(df[&#x27;text&#x27;])

X = pd.concat([df[&#x27;len&#x27;], df[&#x27;punct%&#x27;], pd.DataFrame(X_tfidf.toarray())], axis=1)</pre>
  <figure id="gOcg" class="m_original">
    <img src="https://img1.teletype.in/files/c1/78/c178182e-f345-47cb-a0bf-74d7a62523af.png" width="755" />
    <figcaption>Итоговые данные которые будут использоваться как входные (X)</figcaption>
  </figure>
  <p id="v5dQ">Таблица выше это то что стало с нашей колонкой &quot;text&quot;. Мы будем использовать эту таблицу как параметры X. Как если бы мы писали функцию y = f(X). То колона &quot;label&quot; будет y, а f это наша модель о которой мы поговорим в следующем этапе.</p>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="QLKO">Давайте попробуем разбить все выше упомянутые предложения в таблицу TFIDF Vectorisation:</p>
  </section>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <pre id="qUPA" data-lang="python">from sklearn.feature_extraction.text import TfidfVectorizer

documents = [
    &quot;batman&quot;,
    &quot;writ articl bor&quot;,
    &quot;writ articl bor topic interest&quot;
]
tfidf_vectorizer = TfidfVectorizer()

tfidf_matrix = tfidf_vectorizer.fit_transform(documents)

tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out())
tfidf_df</pre>
  </section>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <figure id="bryU" class="m_custom">
      <img src="https://img3.teletype.in/files/66/58/6658b04f-8368-43f8-b984-f5352dca4cb6.png" width="344" />
    </figure>
  </section>
  <h2 id="OyPn">Обучение модели (Model Training)</h2>
  <p id="Qz4d">Для обучения, я буду использовать Random Forest Classifier*. Основные преимущества для данной задачи в том что оно может обрабатывать большие наборы данных и эффективно использовать признаки. Но что более важно это то что спам тексты составляют всего 13% от общего количества. В классификации классы дисбалансированы (меньшее количество спам-сообщений по сравнению с обычными). Random Forest может компенсировать этот дисбаланс за счет выбора случайных подмножеств данных и голосования.</p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="TCOL"><strong>Random Forest Classifier</strong> — это алгоритм машинного обучения для решения задач классификации и регрессии, основанный на ансамбле решений. Он состоит из множества <strong>решающих деревьев</strong>, работающих вместе для повышения точности предсказаний. Основная идея Random Forest заключается в том, чтобы создать &quot;лес&quot; из нескольких деревьев решений, где каждое дерево обучается на случайном подмножестве данных и признаков. Итоговый прогноз делается на основе <strong>голосования</strong> всех деревьев.</p>
  </section>
  <h3 id="lvpz">Шаг 1:</h3>
  <p id="fA27">Разделяем данные на тестовый и тренировочный набор в соотношении 20/80. Будем тренировать на тренировочном наборе, затем проверять и оценивать на тестовом.</p>
  <pre id="w0Zt" data-lang="python">X_train, X_test, y_train, y_test = train_test_split(X, df[&#x27;label&#x27;], test_size=0.2)</pre>
  <h3 id="KyGI">Шаг 2:</h3>
  <p id="zTCe">Инициализируем модель и загружаем наши данные.</p>
  <pre id="aDq5" data-lang="python">rf = RandomForestClassifier(n_estimators=100)
rf_model = rf.fit(X_train, y_train)</pre>
  <section style="background-color:hsl(hsl(24,  24%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="SuEM">Random Forest имеет такую функцию как feature_importances_, которая показывает степень важности параметра в обучении. Давайте рассмотрим самые полезные из них</p>
    <pre id="BVop" data-lang="python">sorted(zip(rf_model.feature_importances_, X_train.columns), reverse=True)[:10]</pre>
    <p id="7xQ6">Результат:<br />[(0.05284560707873848, &#x27;8038&#x27;),<br /> (0.038103717348420954, &#x27;len&#x27;),<br /> (0.03153365424945696, &#x27;1789&#x27;),<br /> (0.02343684600283975, &#x27;7292&#x27;),<br /> (0.021851042514375484, &#x27;4765&#x27;),<br /> (0.02169782852359817, &#x27;3118&#x27;),<br /> (0.017913250216369424, &#x27;2018&#x27;),<br /> (0.015055476550308528, &#x27;6695&#x27;),<br /> (0.014056416317266856, &#x27;7402&#x27;),<br /> (0.012347618727250731, &#x27;5945&#x27;)]</p>
    <p id="ed9Q">Значение &#x27;len&#x27; находится в топ 2, значит наша гипотеза и анализ были верны.</p>
  </section>
  <h3 id="TwWD">Шаг 3:</h3>
  <p id="0sxO">Проверяем и оцениваем:</p>
  <pre id="w2fa" data-lang="python">from sklearn.metrics import precision_recall_fscore_support as score
y_pred = rf_model.predict(X_test)
precision, recall, fscore, support = score(y_test, y_pred, pos_label=&#x27;spam&#x27;, average=&#x27;binary&#x27;)
print(f&#x27;Precision: {precision}\nRecall: {recall}\nFscore: {fscore}\nAccuracy: {(y_pred==y_test).sum() / len(y_pred)}&#x27;)</pre>
  <p id="NIy5">Результат:<br />Precision*: 1.0<br />Recall*: 0.8282208588957055<br />Fscore*: 0.9060402684563758<br />Accuracy*: 0.9748878923766816</p>
  <section style="background-color:hsl(hsl(55,  86%, var(--autocolor-background-lightness, 95%)), 85%, 85%);">
    <p id="exy0"><strong>Precision score</strong> (точность) — это метрика, используемая для оценки качества модели классификации. Precision показывает, какую долю объектов, предсказанных как положительные(spam), модель действительно классифицировала правильно.</p>
    <p id="lSOp"><strong>Recall</strong> (или полнота) — это метрика, используемая для оценки качества модели машинного обучения, особенно в задачах классификации. Она показывает, как хорошо модель находит все <strong>положительные примеры</strong> (spam) из всех возможных.</p>
    <p id="b2Z1"><strong>F-score</strong> (или <strong>F1-score</strong>) — это метрика качества модели, которая учитывает как <strong>точность</strong> (precision), так и <strong>полноту</strong>(recall). Она особенно полезна в задачах, где классы несбалансированы как при классификации спам/не спам.</p>
    <p id="BNCl"><strong>Accuracy score</strong> (точность) — это метрика оценки качества модели классификации, которая показывает, какую долю правильных предсказаний модель сделала от общего числа предсказаний. Она используется для измерения производительности модели.</p>
  </section>
  <h2 id="9HIO">Заключение (Conclusion)</h2>
  <p id="SFwP">Реальный код продолжается далее уже с использованием других моделей с разными параметрами, но думаю на этом с вас хватит. Спасибо всем кто читал данную статейку. </p>

]]></content:encoded></item></channel></rss>