Serbian News Aggregator

2025-2026

Two abstract information streams pass through a classifier and form three clusters.

The project began as a scraper. It became more useful when I treated the resulting archive as a measurement system: a way to ask what separates two newsrooms, how much of that difference is topic rather than style, and whether a model can reproduce the distinction.

The analysis uses 2,304 articles published in January 2026: 1,963 from N1 Srbija and 341 from Biznis.rs. I developed it across three machine-learning assignments. They overlap heavily, so this page presents them as one cumulative study rather than three separate papers.

How the three iterations fit together

Iteration New question Main addition
1 Can article text identify the outlet? TF-IDF, logistic regression, Naive Bayes, LinearSVC
2 How much can the representation be compressed? LSA and a neural autoencoder
3 What signal is the classifier actually using? BERTić, metadata ablation, NMF topics, retrieval, and mT5 generation

The corpus, preprocessing, TF-IDF baseline, and most of the evaluation machinery recur in all three. The real progression is from prediction, to compression, to interpretation.

The question kept changing

I did not begin with a theory about editorial style. I began with a practical annoyance: collecting articles from different sites produces records that look similar but are never quite interchangeable. Each source has its own page structure, naming conventions, missing fields, and repeated boilerplate. Building the scraper forced me to decide what an article actually is before I could analyze one.

Once the data was clean enough to compare, the first classification score was almost suspiciously high. That changed the project. “Can a model distinguish the outlets?” was no longer a very interesting question; clearly it could. The better question was why it could. Was it learning writing style, subject matter, author signatures, page furniture, or some mixture of all four?

Each iteration therefore tried to remove one easy explanation. The first established a careful baseline under class imbalance. The second asked whether the signal was spread across thousands of terms or concentrated in a much smaller space. The third opened the model up: remove metadata, inspect coefficients and topics, compare contextual embeddings, and finally ask whether the learned distinction could be controlled during generation.

From web pages to a corpus

Two Scrapy spiders walk each outlet's archive, follow article pages, and emit the same record shape for title, URL, timestamp, category, author, body, images, and embedded media. Source-specific parsing stays inside the collector. Downstream analysis sees one stable table.

That separation is the part of the engineering I care about most. A crawler should be allowed to break when a publisher changes its markup without forcing every later notebook to change with it. The normalized article record became the boundary between acquisition and analysis. It also made failures visible: an empty author is a missing value; an empty body is a broken extraction.

The imbalance is substantial: N1 supplied almost six times as many articles. Biznis articles were also longer at the median: 2,607 characters versus 1,574 for N1. That matters because an apparently strong classifier can simply learn the majority class or other collection artifacts.

For classical models, I joined paragraphs, lowercased and normalized Serbian text, transliterated Cyrillic to Latin, removed Serbian stopwords, and built unigram and bigram TF-IDF features. A typical article activates only a tiny fraction of the roughly 39,000 resulting features.

Transliteration was a modeling choice, not just cleaning. It stops the same Serbian word written in two scripts from becoming two unrelated features, but it also removes script choice as a possible editorial signal. I accepted that trade-off because I wanted the first model to compare vocabulary rather than typography. Bigrams remained useful because phrases such as institutional names and market expressions carry more information than their individual words.

The weight of term tt in document dd is:

tfidf⁡(t,d)=(1+log⁡ft,d)log⁡(N1+df⁡(t))\operatorname{tfidf}(t,d) = \bigl(1+\log f_{t,d}\bigr) \log\left(\frac{N}{1+\operatorname{df}(t)}\right)

This deliberately simple representation preserves the lexical choices that turned out to matter most.

An honest evaluation contract

The first iteration used stratified five-fold cross-validation, then reduced the N1 class inside each training fold only. The validation fold kept the original 6:1 prevalence. Ten resamples per fold produced 50 fits per configuration.

The order is important. Balancing the whole dataset before splitting would make evaluation easier and would no longer answer how the model behaves on the distribution it will actually see. Resampling only the training fold gives the learner equal exposure to both outlets while leaving the test unchanged. Repeating the random undersampling also reveals whether a result depends on one fortunate selection of N1 articles.

for train_idx, val_idx in folds.split(X, y):
    minority = train_idx[y[train_idx] == "biznis"]
    majority = train_idx[y[train_idx] == "n1"]

    sampled = rng.choice(
        majority,
        size=len(minority),
        replace=False,
    )
    balanced_train = np.r_[minority, sampled]

    model.fit(X[balanced_train], y[balanced_train])
    predictions = model.predict(X[val_idx])

Because accuracy can hide minority-class failure, I used macro F1 as the primary score:

F1macro=F1,N1+F1,Biznis2,F1=2precision⋅recallprecision+recall.F_1^{\mathrm{macro}} = \frac{F_{1,\mathrm{N1}}+F_{1,\mathrm{Biznis}}}{2}, \qquad F_1=2\frac{\mathrm{precision}\cdot\mathrm{recall}} {\mathrm{precision}+\mathrm{recall}}.

There is one important wrinkle. The later two assignments first created a balanced 682-article working set and then split it into folds. That is a different, easier evaluation distribution. The original run produced macro F1 0.9685; the later shared comparison pipeline produced 0.9880. I do not treat that increase as a model improvement. I use 0.9880 only when comparing representations evaluated under the same later protocol.

This is also why I report the protocols rather than one “best” number. A score is partly a property of the model, but it is also a property of the split, prevalence, preprocessing, and sampling procedure. Two scores can look comparable while answering different questions.

A sparse linear baseline was hard to beat

The first tuned model was ordinary L2-regularized logistic regression:

P(y=1∣x)=σ(w⊤x+b),Lreg=−∑i[yilog⁡y^i+(1−yi)log⁡(1−y^i)]+λ∥w∥22.P(y=1\mid x)=\sigma(w^\top x+b), \qquad \mathcal L_{\mathrm{reg}} = -\sum_i\left[y_i\log\hat y_i+(1-y_i)\log(1-\hat y_i)\right] +\lambda\lVert w\rVert_2^2.

With C=100C=100 and a liblinear solver, it reached macro F1 0.9685 +/- 0.0109 on the original-distribution validation folds. LinearSVC was effectively tied at 0.9677, while tuned Multinomial Naive Bayes reached 0.8545.

The useful lesson was not that the most elaborate model won. It was that outlet membership was already close to linearly separable in a sparse lexical space.

I initially expected the probabilistic Naive Bayes model to be a strong fit for word counts. It underperformed because its conditional-independence assumption is especially crude here: political names, institutional phrases, financial quantities, and section boilerplate arrive in correlated groups. Logistic regression and LinearSVC can instead assign weights directly to those overlapping indicators.

The very high linear score was useful and uncomfortable. It suggested that the dataset contained a real editorial signature, but it did not say whether that signature was interesting. A model that recognizes an outlet name in the footer is accurate and intellectually useless. The rest of the work was an attempt to tell those two cases apart.

How much of 39,000 dimensions did the model need?

The second iteration held the classifier fixed and changed only the representation. Latent Semantic Analysis approximates the TF-IDF matrix with a rank-kk factorization:

X≈UkΣkVk⊤.X\approx U_k\Sigma_kV_k^\top.

I tested k∈{25,50,100,200}k\in\{25,50,100,200\}. Performance rose quickly from 25 to 50 components, then flattened. At 200 dimensions, LSA matched the roughly 39,000-dimensional TF-IDF baseline.

I chose compression as the second question because a sparse classifier can succeed through thousands of tiny, unrelated clues. If a few hundred latent dimensions retain the result, the distinction is probably more structured: recurring clusters of terms and topics rather than isolated tokens. The experiment does not identify those dimensions as human-readable concepts, but it measures how compact the predictive signal is.

model = Pipeline([
    ("svd", TruncatedSVD(
        n_components=200,
        random_state=42,
    )),
    ("lr", LogisticRegression(
        penalty="l2",
        C=100,
        solver="lbfgs",
        max_iter=2000,
    )),
])

A nonlinear autoencoder compressed the LSA input to a 32-dimensional bottleneck. It reconstructed the input with low error, but classification fell to macro F1 0.9683. Reconstruction quality was not the same thing as preserving the decision boundary. In this small-data regime, the stable linear compression was better than the more complex neural one.

This result changed how I thought about “better representations.” The autoencoder optimized the average input reconstruction, not the particular information that separates outlets. It could preserve common language very well while discarding a small discriminative direction. A more expressive model was not automatically a more useful model because its objective was only adjacent to the task I cared about.

What the classifier was really learning

The third iteration compared four feature spaces using the same logistic-regression head. Raw TF-IDF, LSA, and frozen BERTić sentence embeddings all landed within one standard deviation of each other. The modern encoder provided no measurable advantage under this protocol.

For BERTić, each article was truncated to the first 512 tokens and represented by mean pooling frozen token embeddings. That is a pragmatic baseline, not the strongest possible transformer setup. Long articles lose their endings, mean pooling flattens sentence structure, and the encoder never adapts to this corpus. Still, the comparison was useful: contextual semantics alone did not uncover an obvious advantage over sparse words.

The diagnostic result came from removing bylines, dates, outlet names, wire-service attributions, and standalone numbers. Macro F1 dropped from 0.9880 to 0.9481. About four points of the apparent separation came from publication metadata rather than editorial language.

The ablation mattered more to me than the difference between TF-IDF and BERTić. It turned a vague suspicion about leakage into a measured quantity. It also showed why interpretability should not be something added after training: examining the inputs changed the claim I was willing to make about the model.

The remaining signal was still strong, but it mixed two things that this dataset cannot fully separate: how the outlets write and what they choose to cover.

Coefficient inspection made the distinction concrete. N1-weighted terms formed a political and civic register: vučić, vlasti, protiv, kosova, and stranke. Biznis-weighted terms formed a market register: odsto, kompanija, rast, tržištu, miliona, and cena.

An eight-topic NMF decomposition told the same story. One markets-and-numbers topic accounted for 76.7% of the average topic mass in Biznis articles. N1 spread its attention across general politics, judicial reform, university protests, emergency response, and other civic beats.

That makes “editorial style” too strong a label for the result. Much of the boundary is editorial selection. Biznis.rs publishes more market reporting; N1 publishes more politics and civic affairs. To isolate style, I would need matched pairs about the same event, ideally written at the same time, with metadata removed. This corpus shows that the outlets occupy different textual distributions, not that it has discovered an invariant voice.

A t-SNE projection of 2,304 frozen BERTić article embeddings, with N1 broadly dispersed and Biznis forming tighter clusters. A two-dimensional t-SNE projection of frozen BERTić embeddings. It is an illustration of local geometry, not an independent performance result.

One headline, two outlet prefixes

The final experiment fine-tuned google/mt5-small on (outlet + title) -> article body pairs. At inference, the same headline was sent through the model twice. Only one token changed:

This was not an attempt to automate journalism. I wanted to turn the classifier around. Classification asks whether outlet information is present in an article; conditional generation asks whether the same information is usable as a control. If one prefix changes the vocabulary while the headline stays fixed, the model has learned at least some directional association with the outlet label.

def make_input(source: str, title: str) -> str:
    prefix = "n1" if source == "n1" else "biznis"
    return f"{prefix}: {title}"

In mathematical shorthand:

x=<outlet>: title,<outlet>∈{n1,biznis}.x=\texttt{<outlet>: title}, \qquad \texttt{<outlet>}\in\{\texttt{n1},\texttt{biznis}\}.

Across 150 generated samples, switching from the n1: prefix to biznis: nearly doubled the presence of Biznis-specific markers, from 29.3% to 57.3%.

That is evidence that the control prefix changed the generated vocabulary. It is not evidence that the model could write publishable news. The 300M-parameter model frequently produced poor Serbian grammar and invented names, dates, and numbers. Real/generated top-word overlap was only 4/50 for N1 and 0/50 for Biznis. The result is a controlled representation experiment, not a newsroom product.

What I take from the project

The project made me more skeptical of impressive first results. When a simple model reaches the high nineties, the productive response is not immediately to replace it with a transformer. It is to look for shortcuts, rebuild the evaluation boundary, and ask which parts of the data made the task easy.

It also reinforced a preference for small models as investigative tools. Logistic-regression coefficients, low-rank components, ablations, and deliberately constrained generators each answered a different question. Together they produced a better account of the corpus than any single leaderboard number.

Most importantly, the scraper and the analysis are one system. Collection choices determine the claims the model can support. A missing paragraph, repeated wire copy, retained byline, or one-month sampling window is not merely a data-cleaning detail; it becomes part of the model's behavior. The system is trustworthy only when those choices remain visible from collection through evaluation.

What I would change next

The next version should make the evaluation stricter before making the model larger:

  1. Fit TF-IDF, LSA, and every learned transform inside each training fold.
  2. Deduplicate syndicated or near-identical articles and group related stories into the same fold.
  3. Hold out a later month to measure temporal generalization.
  4. Add more outlets and match articles by topic so editorial style can be separated from beat selection.
  5. Treat generation as a retrieval-grounded, human-evaluated task or leave it out entirely.

The strongest conclusion is modest: simple lexical models captured nearly all measurable source signal, compression preserved most of it, and careful ablation was more informative than adding model complexity.

View the source on GitHub

Систем за прикупљање и анализу српских вести

2025-2026

Два апстрактна тока информација пролазе кроз класификатор и образују три групе.

Пројекат је почео као систем за прикупљање вести. Постао је занимљивији када сам архиву почео да посматрам као мерни инструмент: начин да испитам шта раздваја две редакције, колико те разлике потичу од теме, а колико од стила, и да ли модел може да их понови.

Анализа обухвата 2.304 чланка објављена у јануару 2026. године: 1.963 са портала N1 Србија и 341 са портала Biznis.rs. Развијао сам је кроз три задатка из машинског учења. Велики део материјала се понавља, па их овде представљам као једно истраживање које се постепено развијало.

Како се три итерације надовезују

Итерација Ново питање Главни додатак
1 Може ли текст чланка да открије редакцију? TF-IDF, логистичка регресија, Наивни Бајес и LinearSVC
2 Колико се простор обележја може сажети? LSA и неуронски аутоенкодер
3 Који сигнал класификатор заиста користи? BERTić, уклањање метаподатака, NMF теме, претрага сродних текстова и mT5 генерација

Корпус, припрема текста, TF-IDF основа и највећи део поступка оцењивања понављају се у сва три рада. Стварни развој тече од предвиђања, преко сажимања, до тумачења.

Питање се стално мењало

Нисам почео са теоријом о редакцијском стилу, већ са практичним проблемом: прикупљање чланака са различитих портала даје записе који личе једни на друге, али никада нису потпуно уједначени. Сваки извор има сопствену структуру странице, називе поља, недостајуће податке и понављајуће делове текста. Израда система за прикупљање натерала ме је да прво одредим шта је уопште један чланак.

Када су подаци постали довољно уређени за поређење, први резултат класификације био је готово сумњиво висок. Тада се променио и пројекат. Питање „може ли модел да разликује редакције?” више није било нарочито занимљиво; очигледно је могао. Важније је било зашто може. Да ли учи стил писања, избор тема, потписе аутора, делове странице или мешавину свега тога?

Зато је свака итерација покушала да уклони по једно лако објашњење. Прва је поставила пажљиву основу у условима неуравнотежених класа. Друга је испитала да ли је сигнал расут кроз хиљаде појмова или сабијен у много мањи простор. Трећа је отворила модел: уклони метаподатке, погледај коефицијенте и теме, упореди контекстуалне векторе и на крају испитај да ли се научена разлика може усмеравати при генерисању текста.

Од веб-страница до корпуса

Два Scrapy spider-а пролазе кроз архиву сваког портала, отварају странице чланака и производе записе истог облика: наслов, URL, време објављивања, рубрику, аутора, тело текста, слике и уграђени садржај. Посебности сваког извора остају у систему за прикупљање, док анализа добија једну уједначену табелу.

Управо ми је то раздвајање најважнији инжењерски део пројекта. Када издавач промени HTML странице, систем за прикупљање сме да се поквари, али та промена не треба да примора сваку каснију анализу на преправку. Уједначени запис чланка постао је граница између прикупљања и анализе. Тако су и грешке постале видљиве: празан аутор је недостајућа вредност, а празно тело текста је неуспело издвајање.

Неравнотежа је велика: N1 је објавио скоро шест пута више чланака. Текстови на Biznis.rs били су и дужи по медијани: 2.607 знакова према 1.574 на N1. Због тога наизглед добар модел лако може да научи већинску класу или споредне трагове начина прикупљања.

За класичне моделе спојио сам пасусе, претворио текст у мала слова, пресловио ћирилицу у латиницу, уклонио српске стоп-речи и изградио TF-IDF обележја од појединачних речи и парова речи. Један чланак активира тек мали део од приближно 39.000 обележја.

Пресловљавање је било моделска одлука, а не само чишћење. Оно спречава да иста српска реч написана у два писма постане два неповезана обележја, али истовремено уклања избор писма као могући редакцијски сигнал. Прихватио сам ту размену јер сам желео да први модел пореди речник, а не типографију. Парови речи су остали важни зато што називи институција и тржишни изрази носе више значења као целина него као одвојене речи.

Тежина појма tt у документу dd је:

tfidf⁡(t,d)=(1+log⁡ft,d)log⁡(N1+df⁡(t))\operatorname{tfidf}(t,d) = \bigl(1+\log f_{t,d}\bigr) \log\left(\frac{N}{1+\operatorname{df}(t)}\right)

Ова намерно једноставна представа чува избор речи који се показао као најважнији сигнал.

Поштено оцењивање

У првој итерацији користио сам стратификовану унакрсну проверу са пет делова, а тек затим смањивао N1 класу унутар дела за обучавање. Скуп за проверу задржавао је првобитни однос од приближно 6:1. Десет поновљених узорака по делу дало је 50 обучавања за сваку поставку.

Редослед је важан. Ако бих уравнотежио цео скуп пре поделе, оцењивање би постало лакше и више не би одговарало на питање како се модел понаша на расподели коју ће заиста видети. Узимање једнаког броја примера само у делу за обучавање даје моделу подједнаку изложеност обема редакцијама, док скуп за проверу остаје непромењен. Понављање насумичног узорковања показује и да ли резултат зависи од једног срећног избора N1 чланака.

for train_idx, val_idx in folds.split(X, y):
    minority = train_idx[y[train_idx] == "biznis"]
    majority = train_idx[y[train_idx] == "n1"]

    sampled = rng.choice(
        majority,
        size=len(minority),
        replace=False,
    )
    balanced_train = np.r_[minority, sampled]

    model.fit(X[balanced_train], y[balanced_train])
    predictions = model.predict(X[val_idx])

Пошто тачност може да прикрије слаб учинак на мањинској класи, главна мера је макро F1:

F1macro=F1,N1+F1,Biznis2,F1=2precision⋅recallprecision+recall.F_1^{\mathrm{macro}} = \frac{F_{1,\mathrm{N1}}+F_{1,\mathrm{Biznis}}}{2}, \qquad F_1=2\frac{\mathrm{precision}\cdot\mathrm{recall}} {\mathrm{precision}+\mathrm{recall}}.

Постоји важна разлика између итерација. У друга два рада најпре је направљен уравнотежен радни скуп од 682 чланка, па је тек онда подељен на делове. То је другачији и лакши распоред за оцењивање. Први поступак дао је макро F1 0,9685, а каснији заједнички поступак 0,9880. Тај раст не представљам као побољшање модела; број 0,9880 користим само за поређење представа оцењених истим каснијим поступком.

Зато наводим поступке, а не само један „најбољи” број. Резултат делом припада моделу, али припада и подели података, односу класа, припреми текста и начину узорковања. Два броја могу изгледати упоредиво, а да заправо одговарају на различита питања.

Једноставан линеарни модел било је тешко надмашити

Први подешени модел била је обична логистичка регресија са L2 регуларизацијом:

P(y=1∣x)=σ(w⊤x+b),Lreg=−∑i[yilog⁡y^i+(1−yi)log⁡(1−y^i)]+λ∥w∥22.P(y=1\mid x)=\sigma(w^\top x+b), \qquad \mathcal L_{\mathrm{reg}} = -\sum_i\left[y_i\log\hat y_i+(1-y_i)\log(1-\hat y_i)\right] +\lambda\lVert w\rVert_2^2.

Са C=100C=100 и liblinear алгоритмом достигао је макро F1 0,9685 +/- 0,0109 на скуповима за проверу са првобитном расподелом. LinearSVC је био практично изједначен са 0,9677, док је подешени Мултиномијални Наивни Бајес достигао 0,8545.

Најважније није било то што је најсложенији модел победио. Напротив, припадност редакцији већ је била скоро линеарно раздвојива у ретком лексичком простору.

У почетку сам очекивао да ће вероватносни Наивни Бајес добро одговарати бројању речи. Био је слабији зато што је његова претпоставка о условној независности овде посебно груба: политичка имена, називи институција, финансијски бројеви и понављајући делови странице долазе у повезаним групама. Логистичка регресија и LinearSVC могу непосредно да доделе тежине тим преклопљеним показатељима.

Веома висок резултат линеарног модела био је истовремено користан и непријатан. Указивао је на стварни редакцијски траг у скупу, али није говорио да ли је тај траг занимљив. Модел који препозна назив портала у подножју странице може бити тачан и потпуно бескористан за разумевање. Остатак рада био је покушај да раздвојим та два случаја.

Колико је од 39.000 димензија било потребно?

У другој итерацији класификатор је остао исти, а мењала се само представа текста. Латентна семантичка анализа (LSA) приближава TF-IDF матрицу факторизацијом ранга kk:

X≈UkΣkVk⊤.X\approx U_k\Sigma_kV_k^\top.

Испробао сам k∈{25,50,100,200}k\in\{25,50,100,200\}. Учинак је брзо порастао од 25 до 50 компоненти, а затим се готово зауставио. Са 200 димензија LSA је изједначила TF-IDF основу од приближно 39.000 димензија.

Сажимање сам изабрао као друго питање јер ретки класификатор може да успе ослањајући се на хиљаде ситних, међусобно неповезаних трагова. Ако неколико стотина скривених димензија очува резултат, разлика је вероватно уређенија: понављајуће групе појмова и тема, а не изоловани токени. Оглед не тврди да су те димензије људски разумљиви концепти, али мери колико је предиктивни сигнал збијен.

model = Pipeline([
    ("svd", TruncatedSVD(
        n_components=200,
        random_state=42,
    )),
    ("lr", LogisticRegression(
        penalty="l2",
        C=100,
        solver="lbfgs",
        max_iter=2000,
    )),
])

Нелинеарни аутоенкодер је сажео LSA улаз у уско грло од 32 димензије. Улаз је реконструисао са малом грешком, али је класификација пала на макро F1 0,9683. Добра реконструкција није исто што и очување границе одлучивања. Са овако мало података, стабилно линеарно сажимање било је боље од сложенијег неуронског модела.

Овај резултат је променио моје схватање „бољих представа”. Аутоенкодер је оптимизовао просечну реконструкцију улаза, а не баш ону информацију која раздваја редакције. Могао је веома добро да сачува уобичајени језик и истовремено одбаци мали, али важан правац. Изражајнији модел није аутоматски био и кориснији јер је његов циљ био само приближан питању које ме је занимало.

Шта је класификатор заиста научио

Трећа итерација упоредила је четири простора обележја са истом логистичком регресијом. Сирови TF-IDF, LSA и замрзнути BERTić вектори реченица завршили су унутар једне стандардне девијације. Савремени енкодер није показао мерљиву предност у овом поступку.

За BERTić је сваки чланак скраћен на првих 512 токена, а његова представа добијена просеком замрзнутих вектора токена. То је практична почетна поставка, не најјачи могући трансформерски поступак. Дуги текстови губе завршетак, усредњавање изравнава структуру реченица, а енкодер се не прилагођава овом корпусу. Поређење је ипак било корисно: само контекстуално значење није донело очигледну предност над ретким речима.

Најважнија дијагностичка провера била је уклањање потписа аутора, датума, назива редакције, назива новинских агенција и самосталних бројева. Макро F1 пао је са 0,9880 на 0,9481. Око четири поена привидне раздвојивости долазило је од метаподатака, а не од редакцијског језика.

Ово уклањање било ми је важније од разлике између TF-IDF и BERTić модела. Нејасну сумњу о цурењу информација претворило је у измерену вредност. Показало је и зашто тумачење не треба додавати тек после обучавања: увид у улазе променио је тврдњу коју сам био спреман да изнесем о моделу.

Преостали сигнал и даље је снажан, али спаја две ствари које овај скуп не може потпуно да раздвоји: како редакције пишу и о чему одлучују да пишу.

Коефицијенти модела учинили су разлику видљивом. Речи које су нагињале ка N1 припадале су политичком и грађанском регистру: vučić, vlasti, protiv, kosova и stranke. Речи које су нагињале ка Biznis.rs припадале су тржишном регистру: odsto, kompanija, rast, tržištu, miliona и cena.

NMF модел са осам тема испричао је исту причу. Тема тржишта и бројева чинила је 76,7% просечне тематске тежине у текстовима Biznis.rs. N1 је распоређивао пажњу на општу политику, правосуђе, студентске протесте, ванредне ситуације и друге грађанске теме.

Због тога је „редакцијски стил” прејака ознака за овај резултат. Велики део границе потиче од редакцијског избора тема. Biznis.rs објављује више тржишних вести, а N1 више политичких и друштвених. За издвајање самог стила били би потребни упарени текстови о истом догађају, настали у слично време и без метаподатака. Овај корпус показује да редакције заузимају различите текстуалне расподеле, а не да је пронашао њихов непроменљиви глас.

Дводимензионална t-SNE пројекција 2.304 замрзнута BERTić вектора, са шире распршеним N1 чланцима и збијенијим групама Biznis.rs. Дводимензионална t-SNE пројекција замрзнутих BERTić вектора. Она илуструје локалну геометрију, али није независан доказ квалитета модела.

Један наслов, два редакцијска префикса

Завршни оглед је додатно обучио google/mt5-small на паровима (редакција + наслов) -> тело чланка. При употреби је исти наслов два пута послат моделу, а промењен је само један део улаза:

Ово није био покушај аутоматизације новинарства. Желео сам да окренем класификатор у супротном смеру. Класификација пита да ли се информација о редакцији налази у чланку; условно генерисање пита да ли та информација може да послужи као контрола. Ако један префикс промени речник док наслов остаје исти, модел је научио бар део усмерене везе са ознаком редакције.

def make_input(source: str, title: str) -> str:
    prefix = "n1" if source == "n1" else "biznis"
    return f"{prefix}: {title}"

Математички записано:

x=<редакција>: наслов,<редакција>∈{n1,biznis}.x=\texttt{<редакција>: наслов}, \qquad \texttt{<редакција>}\in\{\texttt{n1},\texttt{biznis}\}.

У 150 генерисаних примера, промена префикса n1: у biznis: скоро је удвостручила присуство израза својствених Biznis.rs, са 29,3% на 57,3%.

То показује да је контролни префикс променио речник модела. Не показује да је модел умео да пише објављиве вести. Модел од 300 милиона параметара често је производио лошу српску граматику и измишљао имена, датуме и бројеве. Преклапање 50 најкарактеристичнијих речи стварног и генерисаног корпуса било је само 4/50 за N1 и 0/50 за Biznis.rs. Ово је контролисан оглед са представама, а не производ за редакцију.

Шта сам извукао из пројекта

Пројекат ме је учинио опрезнијим према упечатљивим првим резултатима. Када једноставан модел достигне вредности близу јединице, продуктиван одговор није да га одмах заменимо трансформером. Треба тражити пречице, поново проверити границу оцењивања и утврдити који су делови података учинили задатак лаким.

Такође је ојачао моју склоност ка малим моделима као истраживачким алатима. Коефицијенти логистичке регресије, компоненте ниског ранга, уклањање група обележја и намерно ограничено генерисање одговарали су на различита питања. Заједно су дали боље објашњење корпуса него било који појединачни резултат.

Најважније је то што систем за прикупљање и анализа чине једну целину. Одлуке при прикупљању одређују које тврдње модел може да подржи. Недостајући пасус, поновљена агенцијска вест, задржан потпис аутора или узорак из само једног месеца нису обични детаљи чишћења; они постају део понашања модела. Систем је поуздан тек када те одлуке остају видљиве од прикупљања до оцењивања.

Шта бих следеће променио

Наредна верзија треба прво да пооштри оцењивање, па тек онда да увећа модел:

  1. TF-IDF, LSA и сваку научену трансформацију треба прилагођавати само на делу за обучавање у сваком пресеку.
  2. Синдиковане и скоро истоветне текстове треба уклонити, а сродне приче држати у истом делу скупа.
  3. Каснији месец треба оставити као временски одвојен скуп за проверу.
  4. Треба додати више редакција и упоређивати текстове о истој теми како би се стил раздвојио од избора области.
  5. Генерисање треба везати за претрагу извора и људско оцењивање - или га потпуно изоставити.

Најснажнији закључак је умерен: једноставни лексички модели ухватили су скоро сав мерљив сигнал о редакцији, сажимање је очувало већину тог сигнала, а пажљива дијагностика дала је више од повећавања сложености модела.

Погледајте изворни код на GitHub-у