šŸ’° Aiz AI ažiotāžas: Kāpēc klasiskā maŔīnmācīŔanās joprojām ir biznesa mugurkauls. Aizdevuma atmaksas prognozēŔana ar ML modeli 92% precizitāti.

Machine Learning
Kaggle
Python
Author

Imants Dolgins

Published

November 19, 2025

Ievads

MÅ«sdienās visi runā par mākslÄ«go intelektu kā par universālu rÄ«ku, kas spēj atrisināt jebkuru problēmu. Tomēr realitātē Å”ie AI modeļi bieži vien darbojas kā ā€œmelnā kasteā€ šŸ“¦ – rezultāti var bÅ«t neprognozējami, un atbilde var atŔķirties pat tad, ja jautājums tiek uzdots identiski. Tas biznesā rada riskus.

TieÅ”i tāpēc klasiskā maŔīnmācīŔanās (Machine Learning) joprojām ir neaizvietojama un kritiski svarÄ«ga. Tā balstās uz matemātisku precizitāti un tiek izmantota daudzās nozarēs, lai pieņemtu izŔķiroÅ”us lēmumus:

  • šŸ¦ Banku sektorā: KredÄ«tu atmaksas risku izvērtēŔanai.
  • šŸ›”ļø ApdroÅ”ināŔanā: KrāpniecÄ«bas gadÄ«jumu atklāŔanai un risku precÄ«zai novērtēŔanai.
  • šŸ„ MedicÄ«nā: SlimÄ«bu diagnostikai un ārstēŔanas plānoÅ”anai.
  • šŸ›’ MazumtirdzniecÄ«bā: PieprasÄ«juma un krājumu prognozēŔanai.
  • šŸƒ Fitnesa nozarē: Lietotāju aktivitātes analÄ«zei un ieņēmumu prognozi, ņemot vērā sezonalitāti.
  • Google: IekŔējās lietojumprogrammas – Google izmanto maŔīnmācīŔanos daudzos savos pakalpojumos, piemēram, Google Photos fotoattēlu kategorizēŔanai šŸ“ø, kā arÄ« tādās funkcijās kā Google Maps ceļojuma laika prognozēŔanai šŸ—ŗļø un teikumu automātiskai pabeigÅ”anai āœļø.
  • Meta: Reklāmu mērÄ·auditorijas atlasei un satura ieteikumiem.
  • LinkedIn: Lai ieteiktu jums piemērotākās darba vakances un kontaktus.

Å ajā rakstā es parādīŔu savu praktisku piemēru no finanÅ”u pasaules: kā var izmantot Python, LightGBM un Optuna, lai izveidotu stabilu modeli, kas ar 92% precizitāti (AUC) prognozē kredÄ«tu atmaksu, pārvērÅ”ot datus reālā biznesa vērtÄ«bā. šŸ‘‡


šŸ” 1. Solis: Datu izpēte (EDA) – Datu stāsts

Pirms mēs varam kaut ko prognozēt, mums ir ā€œjāsadzirdā€, ko dati mums stāsta. Å ajā projektā es veicu dziļu izpēti 5 posmos, lai atrastu slēptās likumsakarÄ«bas.

1.1. Datu struktūra un kvalitāte

Manā rÄ«cÄ«bā bija ~600 000 ierakstu. Pirmais pārsteigums? Datu kvalitāte. Izmantojot df.info() un df.isnull().sum(), tika atklājts, ka datu kopā nav nevienas trÅ«kstoÅ”as vērtÄ«bas. Tas ir rets gadÄ«jums, kas ļāva mums izlaist sarežģīto datu aizpildīŔanas (imputation) posmu un fokusēties uz analÄ«zi.

ā˜‘ļø Train - Data Info
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 593994 entries, 0 to 593993
Data columns (total 13 columns):
 #   Column                Non-Null Count   Dtype  
---  ------                --------------   -----  
 0   id                    593994 non-null  int64  
 1   annual_income         593994 non-null  float64
 2   debt_to_income_ratio  593994 non-null  float64
 3   credit_score          593994 non-null  int64  
 4   loan_amount           593994 non-null  float64
 5   interest_rate         593994 non-null  float64
 6   gender                593994 non-null  object 
 7   marital_status        593994 non-null  object 
 8   education_level       593994 non-null  object 
 9   employment_status     593994 non-null  object 
 10  loan_purpose          593994 non-null  object 
 11  grade_subgrade        593994 non-null  object 
 12  loan_paid_back        593994 non-null  float64
dtypes: float64(5), int64(2), object(6)
memory usage: 58.9+ MB

1.2. Mērķa mainīgais (Target Variable)

Es sāku ar galveno jautājumu: cik daudzi cilvēki tieŔām atmaksā kredÄ«tus? Tika atklāts mērens disbalanss:

  • āœ… 80% atmaksā kredÄ«tu.
  • āŒ 20% neatmaksā (default).

Kredītu atmaksas sadalījums

Kredītu atmaksas sadalījums

Tas nozÄ«mē, ka mums jābÅ«t uzmanÄ«giem – ja modelis vienkārÅ”i minētu ā€œVisi atmaksāsā€, tā precizitāte bÅ«tu 80%, bet tas bÅ«tu bezjēdzÄ«gs. Tāpēc tika izvēlēta AUC-ROC metrika un stratificētā validācija.

count_paid = train_df['loan_paid_back'].value_counts().get(1.0, 0)
count_not_paid = train_df['loan_paid_back'].value_counts().get(0.0, 0)

1.3. Skaitlisko pazīmju analīze

Tālāk es pētÄ«ju tādus rādÄ«tājus kā Gada ienākumi, Parāda-ienākumu attiecÄ«ba (DTI) un KredÄ«treitings. Vizuālā analÄ«ze (histogrammas un ā€œbox plotsā€) atklāja divas bÅ«tiskas lietas:

  1. Izteikta nobīde (Skewness): Ienākumi un DTI rādītāji bija spēcīgi nobīdīti pa labi (right-skewed). Vēlāk es to laboju ar log1p transformāciju.
  2. SpēcÄ«gi signāli: Bija skaidri redzama atŔķirÄ«ba – cilvēkiem, kas neatmaksā kredÄ«tus (sarkanie grafikos), parasti ir zemāks kredÄ«treitings un augstāka parāda slodze.

Sadalījums 1

Sadalījums 1

Sadalījums 2

Sadalījums 2

Sadalījums 3

Sadalījums 3

Sadalījums 4

Sadalījums 4

Sadalījums 5

Sadalījums 5

1.4. Kategorisko pazīmju analīze

Å is bija viens no interesantākajiem posmiem. Es analizēju, kā atmaksas varbÅ«tÄ«ba mainās atkarÄ«bā no profesijas vai kredÄ«ta mērÄ·a. Atklājums bija dramatisks, bet paÅ”saprotams:

  • šŸŽ“ Studenti un Bezdarbnieki: Ä»oti augsts risks (tikai ~7-26% atmaksas rādÄ«tājs).
  • šŸ‘” Pensionāri un Nodarbinātie: Ä»oti zems risks (~90-99% atmaksas rādÄ«tājs).
  • šŸ”¤ Grade (KredÄ«ta reitings): Perfekta lineāra sakarÄ«ba – jo zemāka bankas pieŔķirtā klase (no A uz F), jo mazāka iespēja, ka kredÄ«ts tiks atmaksāts.

Šie ieskati man vēlāk ļāva izveidot spēcīgas funkcijas (features).

Kategoriskā analīze 1

Kategoriskā analīze 1

Kategoriskā analīze 2

Kategoriskā analīze 2

Kategoriskā analīze 3

Kategoriskā analīze 3

Kategoriskā analīze 4

Kategoriskā analīze 4

Kategoriskā analīze 5

Kategoriskā analīze 5

Kategoriskā analīze 6

Kategoriskā analīze 6

1.5. Korelāciju un Mijiedarbību analīze

Visbeidzot, es pārbaudÄ«ju, kā mainÄ«gie ir saistÄ«ti savā starpā. Tika izveidots ā€œPair Plotā€ un korelācijas matrica, kas parādÄ«ja loÄ£isku, bet svarÄ«gu sakarÄ«bu: Zems kredÄ«treitings = Augstāka procentu likme.

Bija skaidri redzams, ka riskantākie klienti grupējas konkrētos datu apgabalos (zems reitings + augsts DTI), kas man deva ideju izveidot jaunas ā€œkombinētāsā€ funkcijas nākamajā solÄ«.

Korelācijas matrica

Korelācijas matrica

Pair Plot analīze

Pair Plot analīze

🧩 2. Solis: Funkciju inženierija (Feature Engineering) – Modeļa ā€œdegvielaā€

Neapstrādāti dati reti ir pietiekami, lai sasniegtu izcilus rezultātus. Funkciju inženierija ir process, kurā mēs izmantojam savas zināŔanas par nozari (Å”ajā gadÄ«jumā – finansēm), lai izveidotu jaunus, spēcÄ«gus mainÄ«gos. Å eit ir mana stratēģija:

2.1. TrokŔņa novērÅ”ana

Pirmais solis bija vienkārÅ”s, bet efektÄ«vs: tika izdzēsti gender (dzimums) un marital_status (Ä£imenes stāvoklis). Mans EDA pierādÄ«ja, ka Å”iem datiem nav nekādas saistÄ«bas ar kredÄ«ta atmaksu. Mazāk ā€œtrokŔņaā€ nozÄ«mē stabilāku modeli.

2.2. ā€œGudrÄā€ kodēŔana (Smart Encoding)

Datos bija kolonna grade_subgrade (piem., ā€œC3ā€), kas bija ļoti svarÄ«ga, bet teksta formātā modelis to nesaprot. Tā vietā, lai izmantotu standarta ā€œOne-Hotā€ kodēŔanu (kas radÄ«tu 35 jaunas kolonnas), mēs izmantojām kārtas kodēŔanu (Ordinal Encoding):

  • Burtu (A-F) pārvērtu ciparā (A=1, B=2…).
  • Apvienoju to ar apakÅ”klasi (3), iegÅ«stot jaunu skaitlisku vērtÄ«bu grade_combined (piem., C3 -> 33).

Rezultāts: Modelis uzreiz ā€œsaprotā€, ka C3 ir riskantāks par A1, bet droŔāks par F5.

2.3. FinanŔu rādītāju izveide (Domain Knowledge)

Šis bija interesants instruments. Es izveidoju jaunas pazīmes, balstoties uz banku loģiku:

  • šŸ’° loan_to_income: KredÄ«ta summa dalÄ«ta ar gada ienākumiem. Vai aizņēmums ir samērÄ«gs ar algu?
  • šŸ“‰ total_debt: Gada ienākumi reizināti ar DTI (Parāda-ienākumu attiecÄ«bu). Tas parāda kopējo parādu apjomu dolāros.
  • šŸ  available_income: Cik naudas klientam paliek pāri pēc parādu nomaksas?
  • āš–ļø affordability: Vai brÄ«vie lÄ«dzekļi sedz ikmēneÅ”a maksājumu?

2.4. Matemātiskās transformācijas

Tā kā ienākumu dati bija ļoti nevienmērÄ«gi (ar dažiem miljonāriem, kas kropļo vidējos rādÄ«tājus), es izmantoju Logaritmisko transformāciju (np.log1p). Tas padarÄ«ja datu sadalÄ«jumu ā€œnormālākuā€ un vieglāk uztveramu modelim.

šŸ’» Koda ieskats: PazÄ«mju izveides funkcija

def create_features(df):
    # PārvērÅ”am reitingus skaitļos (A->1, B->2...)
    grade_map = {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7}
    df['grade_rank'] = df['grade_subgrade'].str[0].map(grade_map)
    
    # Izveidojam finanŔu attiecības
    df['loan_to_income'] = df['loan_amount'] / (df['annual_income'] + 1)
    df['total_debt'] = df['debt_to_income_ratio'] * df['annual_income']
    df['available_income'] = df['annual_income'] - df['total_debt']
    
    # Logaritmiskā transformācija nobīdītiem datiem
    df['log_annual_income'] = np.log1p(df['annual_income'])
    
    return df

šŸ’» Transformēto datu informācija

šŸ“Š New Train Data Info
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 593994 entries, 0 to 593993
Data columns (total 26 columns):
 #   Column                Non-Null Count   Dtype  
---  ------                --------------   -----  
 0   id                    593994 non-null  int64  
 1   annual_income         593994 non-null  float64
 2   debt_to_income_ratio  593994 non-null  float64
 3   credit_score          593994 non-null  int64  
 4   loan_amount           593994 non-null  float64
 5   interest_rate         593994 non-null  float64
 6   education_level       593994 non-null  object 
 7   employment_status     593994 non-null  object 
 8   loan_purpose          593994 non-null  object 
 9   loan_paid_back        593994 non-null  float64
 10  grade_rank            593994 non-null  int64  
 11  grade_number          593994 non-null  int64  
 12  grade_combined        593994 non-null  int64  
 13  loan_to_income        593994 non-null  float64
 14  total_debt            593994 non-null  float64
 15  available_income      593994 non-null  float64
 16  monthly_payment       593994 non-null  float64
 17  payment_to_income     593994 non-null  float64
 18  affordability         593994 non-null  float64
 19  credit_interest       593994 non-null  float64
 20  income_credit         593994 non-null  float64
 21  log_annual_income     593994 non-null  float64
 22  log_loan_to_income    593994 non-null  float64
 23  log_total_debt        593994 non-null  float64
 24  log_available_income  593994 non-null  float64
 25  log_monthly_payment   593994 non-null  float64
dtypes: float64(18), int64(5), object(3)
memory usage: 117.8+ MB

šŸ¤– 3. Solis: ModelēŔana un Optimizācija – No minējumiem lÄ«dz precizitātei

Datu zinātnē modeļa palaiÅ”ana (model.fit) ir vienkārŔākā daļa. Māksla slēpjas validācijas stratēģijā un optimizācijā. Mēs nepaļāvāmies uz veiksmi – mēs izmantojām sistemātisku pieeju.

3.1. Aizsardzība pret pārmācīŔanos (Overfitting)

Tā vietā, lai sadalÄ«tu datus vienkārŔā 80/20 proporcijā (kas ir riskanti, jo rezultāts ir atkarÄ«gs no tā, kuri dati trāpās testā), es izmantoju 10-kārÅ”u Stratificēto Validāciju (10-Fold Stratified Cross-Validation).

  • Kā tas strādā: Dati tiek sadalÄ«ti 10 daļās, saglabājot 80:20 mērÄ·a attiecÄ«bu katrā daļā.
  • Rezultāts: Mēs iegÅ«stam 10 dažādus modeļus un vienu, statistiski ticamu OOF (Out-of-Fold) rezultātu. Mans gala iesniegums (submission) bija visu 10 modeļu prognožu vidējais rādÄ«tājs, kas ir daudz stabilāks nekā viena modeļa minējums.

3.2. ā€œLielais Trijnieksā€

Es izvēlējos Gradient Boosting algoritmus, kas Å”obrÄ«d dominē tabulāro datu sacensÄ«bās. Es trenēju trÄ«s dažādus modeļus, izmantojot GPU paātrinājumu Kaggle vidē:

  • šŸš€ LightGBM: Ātrs, efektÄ«vs un manā eksperimentā izrādÄ«jās visprecÄ«zākais.
  • 🌲 XGBoost: Klasisks, spēcÄ«gs algoritms, kas pievērsa lielāku uzmanÄ«bu nodarbinātÄ«bas statusam.
  • 🐱 CatBoost: Lieliski tiek galā ar kategorijām un sniedza ā€œhibrÄ«daā€ skatÄ«jumu.

3.3. Hiperparametru optimizācija ar ā€œOptunaā€

Es izmantoju Optuna bibliotēku, lai automatizēti atrastu labākās kombinācijas (piem., learning_rate, num_leaves, depth).

  • Optuna veica 50 eksperimentus (trials) katram modelim.
  • Tas uzlaboja manu LightGBM rezultātu no 0.92225 uz 0.92274. Iespējams, izklausās maz, bet sacensÄ«bās un lielos biznesa apjomos Ŕī atŔķirÄ«ba ir bÅ«tiska.

3.4. Modeļu apvienoŔana (Blending)

Beidzot es izmēģināju Modeļu apvienoÅ”anu (Blending) – paņēmot vidējo rezultātu no visiem trim modeļiem. Tomēr dati parādÄ«ja, ka mans Optimizētais LightGBM viens pats darbojas labāk nekā maisÄ«jums. Datu zinātnē ir svarÄ«gi uzticēties validācijas rezultātiem, nevis intuÄ«cijai, tāpēc es izvēlējāmies vienkārŔāko un spēcÄ«gāko risinājumu.

šŸ’» Koda ieskats: Optuna optimizācija

import optuna
def objective(trial):
    params = {
        'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.05),
        'num_leaves': trial.suggest_int('num_leaves', 20, 150),
        'subsample': trial.suggest_float('subsample', 0.6, 0.95),
        'device': 'gpu' # Izmantojam GPU ātrumam
    }
    model = lgb.LGBMClassifier(**params)
    model.fit(X_train, y_train)
    
    return roc_auc_score(y_val, model.predict_proba(X_val)[:, 1])

study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=50)

šŸ’» Koda ieskats: Finālais LightGBM modelis

import lightgbm as lgb

# šŸ† Labākie parametri, ko atradu ar Optuna
best_params = {
    'n_estimators': 5000,          # Maksimālais koku skaits
    'learning_rate': 0.0327,       # Lēna un stabila mācīŔanās
    'num_leaves': 49,              # Koka sarežģītība
    'max_depth': 5,                # Ierobežots dziļums pret "overfitting"
    'subsample': 0.798,            # Datu daļa katram kokam
    'colsample_bytree': 0.718,     # Pazīmju daļa katram kokam
    'reg_alpha': 0.086,            # L1 Regularizācija
    'reg_lambda': 0.083,           # L2 Regularizācija
    'device': 'gpu',               # GPU paātrinājums šŸš€
    'objective': 'binary',
    'metric': 'auc'
}

# Modeļa inicializēŔana
model = lgb.LGBMClassifier(**best_params)

# Trenējam ar "Early Stopping" (apstājas, ja 100 soļus nav uzlabojumu)
model.fit(
    X_train, y_train,
    eval_set=[(X_val, y_val)],
    callbacks=[lgb.early_stopping(stopping_rounds=100)]
)

šŸ“ˆ 4. Solis: Rezultāti un Biznesa Stratēģija – Ko tas nozÄ«mē bankai? šŸ¦

Datu zinātne nebeidzas ar augstu rezultātu – tā beidzas ar lēmumu. Tā kā Å”is ir klasisks binārās klasifikācijas uzdevums (kur mums jāprognozē viens no diviem iznākumiem: vai klients atmaksās kredÄ«tu, vai nē), precizitāte ir izŔķiroÅ”a. Å is modelis sasniedza AUC 0.9227, kas ir izcils rādÄ«tājs Ŕādas sarežģītÄ«bas problēmai. Bet ko tas reāli dod biznesam? šŸ¤”

4.1. Modeļa kvalitāte ⭐

ROC lÄ«kne pierādÄ«ja, ka modelis spēj stabili atŔķirt labos klientus no sliktajiem. AplÅ«kojot Feature Importance, trÄ«s galvenie faktori ir loÄ£iski un pamatoti:

  • Debt-to-Income Ratio (DTI) šŸ“‰
  • Credit Score šŸ…
  • Interest Rate šŸ“ˆ

ROC Līkne

ROC Līkne

Nozīmīgākās pazīmes (Feature Importance)

Nozīmīgākās pazīmes (Feature Importance)

4.2. Stratēģiskais lēmums: Ķert ā€œsliktosā€ kredÄ«tus šŸŽ£

Vislielākais izaicinājums ir izvēlēties pareizo ā€œslieksniā€ (threshold). Standarta pieejā (50% varbÅ«tÄ«ba) modelis bija pārāk ā€œpiesardzÄ«gsā€ un palaida garām ~40% no sliktajiem kredÄ«tiem. Bankai vai citiem aizdevējiem tie ir milzÄ«gi zaudējumi. šŸ’ø

Rezultāti ar 0.50 slieksni

Rezultāti ar 0.50 slieksni

Šādā gadÄ«jumā var mainÄ«t biznesa lēmumu un mainÄ«t stratēģiju ā™Ÿļø. Pazeminot slieksni uz 0.20, padarot modeli stingrāku.

Rezultāti ar 0.20 slieksni

Rezultāti ar 0.20 slieksni

Rezultāts:

  • šŸ”“ Pirms (Threshold 0.5): Modelis atklāja tikai 62% no nemaksātājiem.
  • 🟢 Pēc (Threshold 0.2): Modelis veiksmÄ«gi identificēja 80% no visiem nemaksātājiem!

Secinājums: Lai gan tas nozīmē, ka mums manuāli jāpārbauda vairāk pieteikumu, bankai tas ir finansiāli izdevīgāk nekā izsniegt aizdevumus, kas nekad netiks atmaksāti.

AplÅ«kojot rezultātus, redzams, ka stratēģijas maiņa atmaksājas. ⭐ Mēs veiksmÄ«gi identificējām un apturējām 95 223 sliktos kredÄ«tus (True Positives), kas citādi bÅ«tu radÄ«juÅ”i tieÅ”us finansiālus zaudējumus. Protams, nekas nav perfekts – 24 277 gadÄ«jumos (False Negatives) modelis tomēr palaida garām nemaksātājus. šŸ’°

Taču ā€œcenaā€ par Å”o augsto droŔību ir 54 528 labi klienti (False Positives), kurus modelis kļūdaini atzÄ«mēja kā riskantus. šŸ¤” Å eit sākas ā€œaprēķināta riska vadÄ«baā€: tā vietā, lai Å”iem cilvēkiem automātiski atteiktu, banka novirza tos uz manuālo pārbaudi. KredÄ«tspeciālista laiks maksā naudu, taču tas ir nesalÄ«dzināmi lētāk nekā izsniegt miljoniem eiro vērtus ā€œtoksiskusā€ kredÄ«tus. ⭐

Tikmēr lielākā daļa – 419 966 uzticami klienti (True Negatives) – saņem ātru, automātisku apstiprinājumu, ļaujot darbiniekiem fokusēties tikai uz sarežģītajiem gadÄ«jumiem. šŸ’°


5. Kopsavilkums un Galvenās Atziņas

Å is projekts uzskatāmi parāda, ka veiksmÄ«gs maŔīnmācīŔanās risinājums nav tikai par koda rakstīŔanu vai modeļa precizitātes procentiem (AUC). Tas ir par biznesa problēmu tulkoÅ”anu datu valodā.

Mēs redzējām, kā sistemātiska pieeja rada vērtību:

  1. Dziļa izpēte (EDA) ļāva mums ā€œsadzirdētā€, ko dati stāsta par klientu uzvedÄ«bu, nevis paļauties uz pieņēmumiem.
  2. Funkciju inženierija un Optuna palīdzēja izspiest maksimumu no pieejamās informācijas, sasniedzot 92% precizitāti.
  3. SliekŔņa pielāgoÅ”ana (Threshold tuning) pierādÄ«ja, ka modelis ir elastÄ«gs instruments — mēs varam izvēlēties bÅ«t konservatÄ«vi (mazāk riska) vai agresÄ«vi (vairāk klientu), atkarÄ«bā no bankas stratēģijas.

Galu galā, Ŕādi modeļi neaizstāj kredÄ«tspeciālistus, bet gan dod viņiem ā€œsuperspējasā€ — iespēju fokusēties tikai uz svarÄ«gāko, kamēr algoritms paveic melno darbu, izsijājot tÅ«kstoÅ”iem pieteikumu sekundes laikā.

Gala vārds

Å is gadÄ«juma pētÄ«jums ir spilgts piemērs tam, kā tehnoloÄ£ijas transformē finanses. Datu zinātne spēj nomainÄ«t ā€œaklu minēŔanuā€ pret aprēķinātu riska vadÄ«bu. šŸ“Šāœ