Machine Learning Academy · leksjon

Domenetilpasning: Medisinsk avbildning med få etiketter

Deltakere vil bruke overføringslæring fra ImageNet på et datasett med røntgenbilder av brystkassen, implementere klassevektet tap for ubalanserte patologier og evaluere AUC-ROC.

Leksjon 4 av 412 trinn

Domenetilpasning: Medisinsk avbildning med få etiketter er en gratis leksjon i Machine Learning Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Machine Learning Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Machine Learning Academy inneholder totalt 4 leksjoner.

Utfordringen innen medisinsk bildebehandling

Medisinsk bildebehandling byr på unike utfordringer for overføringslæring. I motsetning til naturlige fotografier ligner røntgenbilder av brystkassen, MR-bilder og histologiske snitt ikke på ImageNet-bilder: De er gråtonebilder eller har domenespesifikke fargemønstre, de viktige kjennetegnene (lesjoner, knuter og forkalkninger) er subtile og domenespesifikke, og merkede data krever ekspertradiologer — noe som gjør store merkede datasett dyre og sjeldne.

Til tross for disse utfordringene gir ImageNet-forhåndstrente modeller konsekvent bedre resultater enn trening fra grunnen av på oppgaver innen medisinsk bildebehandling, selv når det visuelle uttrykket er betydelig annerledes. Universelle lavnivåkjennetegn (kantdetektorer og teksturfiltre) kan overføres mellom domener og gir en god initialisering som gjør konvergensen raskere og forbedrer generaliseringen når det finnes få etiketter.

CheXpert-datasettet: klassifisering av røntgenbilder med flere etiketter

CheXpert er et referansedatasett med røntgenbilder av brystkassen, med 224 316 bilder og 14 etiketter (Cardiomegaly, Pleural Effusion, Pneumonia, Atelectasis osv.). I scenarioet vårt med få etiketter simulerer vi bruk av bare en liten andel — for eksempel 1 % (omtrent 2 243 bilder) — for å etterligne reelle kliniske situasjoner der budsjettet for annotering er begrenset.

Dette er et klassifiseringsproblem med flere etiketter: Hvert bilde kan ha flere patologier samtidig, i motsetning til klassifisering med én etikett. Målet er en vektor med 14 binære verdier, og vi bruker Binary Cross-Entropy with Logits elementvis. Evalueringen bruker AUC-ROC for hver patologi, beregnet som gjennomsnittet av alle de 14 etikettene.

# Dataset setup (pseudo-code for illustration)
import torch
from torch.utils.data import Dataset
from PIL import Image
import pandas as pd

class CheXpertDataset(Dataset):
    def __init__(self, csv_path, img_dir, transform=None):
        self.df = pd.read_csv(csv_path)
        self.img_dir = img_dir
        self.transform = transform
        self.labels = ['Atelectasis', 'Cardiomegaly',
                       'Consolidation', 'Edema', 'Pleural Effusion']

    def __len__(self):
        return len(self.df)

    def __getitem__(self, idx):
        img_path = self.df.iloc[idx]['Path']
        img = Image.open(img_path).convert('RGB')  # Convert grayscale to 3-ch
        label = torch.tensor(self.df.iloc[idx][self.labels].values.astype(float))
        if self.transform:
            img = self.transform(img)
        return img, label

Konvertering fra gråtone til RGB for forhåndstrente modeller

De fleste medisinske bilder (røntgenbilder og CT-bilder) er gråtonebilder (én kanal), mens ImageNet-forhåndstrente modeller forventer RGB-inndata med tre kanaler. Den enkleste løsningen er Image.convert('RGB'), som kopierer den ene kanalen tre ganger, eller transforms.Grayscale(num_output_channels=3) i transformasjonspipelinen.

Dette er litt sløsing — de tre kanalene er identiske — men i praksis fungerer det godt fordi modellen ganske enkelt lærer å vekte alle tre kanalene likt. Et alternativ er å erstatte det første konvolusjonslaget med et nytt Conv2d(1, 64, kernel_size=7, ...) og initialisere det ved å beregne gjennomsnittet av vektene for de tre inngangskanalene. Dette er mer prinsipielt, men gjør treningen mer kompleks.

from torchvision import transforms

# Method 1: Replicate channel at loading time (simplest)
# img = Image.open(path).convert('RGB')

# Method 2: Use transforms
medical_transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.Grayscale(num_output_channels=3),  # 1 -> 3 channels
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225])
])

# Method 3: Modify first conv layer for single-channel input
import torchvision.models as models
model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
# Average the 3-channel weights into 1 channel
w = model.conv1.weight.mean(dim=1, keepdim=True)
model.conv1 = torch.nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)
model.conv1.weight.data = w

Tap for flere etiketter: BCE with Logits

Ved klassifisering med flere etiketter er hver etikett en uavhengig binær prediksjon. Vi bruker nn.BCEWithLogitsLoss() på en vektor med 14 logitter. Tapet beregnes elementvis og gjennomsnittberegnes over både de 14 klassene og batchstørrelsen.

Ubalanserte etiketter er en stor utfordring innen medisinsk bildebehandling: Bare 5–10 % av bildene har Pneumonia eller Consolidation, mens 40–60 % har Pleural Effusion. Send pos_weight til BCEWithLogitsLoss for å oppvek­te den sjeldne positive klassen: En pos_weight på 10 gjør at modellen legger 10× mer vekt på positive eksempler på den aktuelle patologien.

import torch
import torch.nn as nn

# Multi-label BCE loss
criterion = nn.BCEWithLogitsLoss()

# With class weighting for imbalanced pathologies
# Compute pos_weight from training data frequencies
pos_counts = train_labels.sum(dim=0)  # Positives per class
neg_counts = len(train_labels) - pos_counts
pos_weight = (neg_counts / pos_counts.clamp(min=1)).clamp(max=20)  # Cap at 20

criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)

print('pos_weight per label:', pos_weight)
# High values mean that class is rare and needs more emphasis

Modellarkitektur for utdata med flere etiketter

Erstatt klassifiseringshodet i ResNet-50 med et lineært lag som produserer 14 logitter (én per patologi), i stedet for standardverdien 1000 for ImageNet. Vi bruker ikke sigmoid i forward-passeringen — BCEWithLogitsLoss gjør dette internt for numerisk stabilitet. Ved inferens bruker du sigmoid manuelt for å få sannsynligheter.

Det er spesielt viktig å legge til et dropout-lag før det siste lineære laget når datamengden er liten, fordi regularisering hindrer det lille hodet i å overtilpasse. En dropout-sannsynlighet på 0,3–0,5 er vanlig ved finjustering for medisinsk bildebehandling.

import torchvision.models as models
import torch.nn as nn

NUM_CLASSES = 14  # One per CheXpert pathology label

model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)

# Replace with multi-label head
model.fc = nn.Sequential(
    nn.Dropout(p=0.4),
    nn.Linear(model.fc.in_features, NUM_CLASSES)
)
# No sigmoid here - BCEWithLogitsLoss handles it internally

print('Model output shape for batch of 16:',
      model(torch.randn(16, 3, 224, 224)).shape)  # (16, 14)

AUC-ROC: Den riktige måleenheten for medisinske oppgaver

Nøyaktighet er ubrukelig for ubalanserte medisinske datasett. Hvis bare 5 % av pasientene har Pneumonia, oppnår en modell som alltid predikerer «ingen lungebetennelse» 95 % nøyaktighet, samtidig som den er fullstendig ubrukelig klinisk. AUC-ROC (arealet under ROC-kurven) måler evnen til å skille mellom klasser på tvers av alle terskler.

Vi beregner AUC-ROC separat for hver av de 14 patologiene og rapporterer gjennomsnittlig AUC på tvers av alle etikettene. AUC på 0,5 tilsvarer tilfeldig gjetting, 0,7 er akseptabelt, 0,85 eller høyere er på klinisk nivå, og 0,9 eller høyere nærmer seg ofte prestasjonene til radiologer. roc_auc_score i scikit-learn beregner dette effektivt.

import numpy as np
from sklearn.metrics import roc_auc_score
import torch

def evaluate_auc(model, loader, device):
    model.eval()
    all_logits, all_labels = [], []
    with torch.no_grad():
        for images, labels in loader:
            logits = model(images.to(device))
            all_logits.append(torch.sigmoid(logits).cpu().numpy())
            all_labels.append(labels.numpy())
    probs = np.vstack(all_logits)    # (N, 14)
    targets = np.vstack(all_labels)  # (N, 14)
    # AUC per class, then average
    aucs = [roc_auc_score(targets[:, i], probs[:, i])
            for i in range(targets.shape[1])]
    return np.mean(aucs), aucs

Trening med få etiketter: Viktige teknikker

Når du bare har noen hundre eller noen få tusen merkede medisinske bilder, kan flere teknikker hjelpe deg med å utnytte de tilgjengelige dataene best mulig. Kraftig dataaugmentering er det viktigste: Tilfeldige speilvendinger, rotasjoner samt variasjon i kontrast og lysstyrke hjelper, samtidig som augmenteringene må være klinisk realistiske (et røntgenbilde av brystkassen bør ikke vendes vertikalt — det ville aldri forekomme i klinisk praksis).

Progressiv endring av oppløsning (først trening med lavere oppløsning, deretter økning) er en annen effektiv teknikk. Start med 128×128 for å iterere raskt, og finjuster deretter med 224×224 eller til og med 320×320 for å oppnå best mulig nøyaktighet. Dette er mye raskere enn å alltid trene med full oppløsning, og gir ofte samme nøyaktighet som trening med full oppløsning.

from torchvision import transforms

# Clinically appropriate augmentations for chest X-rays
medical_aug = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.85, 1.0)),  # Mild crop
    transforms.RandomHorizontalFlip(p=0.5),                 # OK: X-rays can be flipped
    # transforms.RandomVerticalFlip(p=0.5),                 # NOT OK: clinically invalid
    transforms.ColorJitter(brightness=0.2, contrast=0.3),  # Simulate scan variation
    transforms.RandomRotation(degrees=10),                  # Slight mis-alignment
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406],
                         [0.229, 0.224, 0.225])
])

Selvovervåket forhåndstrening for medisinsk bildebehandling

Når ImageNet-funksjoner ikke overføres godt nok, er selvovervåket forhåndstrening på umerkede medisinske bilder et kraftig alternativ. Metoder som SimCLR, MoCo og DINO lærer representasjoner ved å trene modellen til å kjenne igjen at to augmenterte versjoner av det samme bildet ligner på hverandre, uten bruk av etiketter.

Arbeidsflyten er: (1) forhåndstren på store, umerkede medisinske datasett (CheXpert har 224 000 umerkede bilder), (2) finjuster med det lille merkede datasettet. Dette gir konsekvent bedre resultater enn overføring fra ImageNet på medisinske oppgaver, fordi modellen lærer kjennetegn som er spesifikke for det medisinske domenet, i stedet for kjennetegn for generell objektgjenkjenning.

# Self-supervised pre-training concept (SimCLR-style)
# No labels needed during this phase

class SimCLRLoss(torch.nn.Module):
    def __init__(self, temperature=0.07):
        super().__init__()
        self.temperature = temperature

    def forward(self, z_i, z_j):
        # z_i, z_j: augmented views of the same images
        # Maximise agreement between paired views
        # Minimise agreement between all other pairs in batch
        z = torch.cat([z_i, z_j], dim=0)
        z = torch.nn.functional.normalize(z, dim=1)
        sim = torch.matmul(z, z.T) / self.temperature
        # Contrastive loss computation...
        return sim  # Simplified illustration

Usikkerhetskvantifisering i medisinsk KI

Ved medisinsk beslutningsstøtte er det like viktig å vite hvor sikker en modell er som selve prediksjonen. En modell som sier «Pneumonia: 95 % sannsynlighet», bør stoles mer på enn en som sier «52 % sannsynlighet». Standard sannsynligheter fra softmax er ofte overdrevent sikre og representerer ikke den faktiske usikkerheten.

Monte Carlo Dropout (MC Dropout) tilnærmer seg bayesiansk usikkerhet ved å holde dropout aktiv under inferens og kjøre forward-passeringen flere ganger. Variansen i prediksjonene på tvers av kjøringene anslår usikkerheten. Prediksjoner med høy varians bør flagges for menneskelig vurdering i stedet for å brukes automatisk.

import torch

def mc_dropout_predict(model, x, n_samples=30):
    model.train()  # Keep dropout active during inference
    predictions = []
    with torch.no_grad():
        for _ in range(n_samples):
            logits = model(x)
            probs = torch.sigmoid(logits)
            predictions.append(probs)
    preds = torch.stack(predictions)  # (n_samples, batch, num_classes)
    mean_pred = preds.mean(dim=0)     # Average prediction
    uncertainty = preds.std(dim=0)    # Std = uncertainty estimate
    return mean_pred, uncertainty

# High uncertainty cases should be reviewed by a radiologist
MEAN_THRESHOLD = 0.5
UNCERTAINTY_THRESHOLD = 0.15

Etiske hensyn ved medisinsk ML

Distribusjon av ML-modeller i medisinske miljøer medfører et stort etisk ansvar. Skjevheter i datasettet er en kritisk bekymring: Modeller som hovedsakelig er trent på bilder fra én sykehusinstitusjons skannere, pasientgrupper eller skannerprodusenter, kan svikte på bilder fra andre miljøer — et kjent problem innen radiologisk KI.

Før distribusjon må du alltid evaluere modellens ytelse på tvers av demografiske undergrupper (alder, kjønn og etnisitet) og på tvers av forskjellig skanneutstyr. Regelverk som FDAs retningslinjer for AI/ML-basert Software as a Medical Device (SaMD) i USA og EUs Medical Device Regulation (MDR) krever grundig klinisk validering før distribusjon. Modeller bør assistere radiologer, ikke erstatte dem — særlig ved alvorlige patologier der feil kan få livstruende konsekvenser.

Hurtigsjekk

Test forståelsen din av domenetilpasning for medisinsk bildebehandling fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen lærte du at overføringslæring for medisinsk bildebehandling bruker ImageNet-forhåndstrente modeller på domener med gråtonebilder og få etiketter ved å konvertere kanaler og erstatte klassifiseringshodet, at BCE-tap med flere etiketter og pos_weight håndterer den store klasseubalansen som er typisk for datasett med patologier, og at AUC-ROC er den riktige evalueringsmåleenheten fordi den måler evnen til å skille mellom klasser uavhengig av klassebalansen. Når du nå behersker disse ferdighetene innen overføringslæring, er du klar til å utforske NLP med BERT i neste kurs.

Gratis å komme i gang

Lær deg Python med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Domenetilpasning: Medisinsk avbildning med få etiketter» gratis?

Ja – hele teksten i «Domenetilpasning: Medisinsk avbildning med få etiketter» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Machine Learning Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Machine Learning Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Domenetilpasning: Medisinsk avbildning med få etiketter»?

Deltakere vil bruke overføringslæring fra ImageNet på et datasett med røntgenbilder av brystkassen, implementere klassevektet tap for ubalanserte patologier og evaluere AUC-ROC. Du øver på Machine Learning Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Machine Learning Academy?

Ingen tidligere erfaring er nødvendig. Machine Learning Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Domenetilpasning: Medisinsk avbildning med få etiketter»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Machine Learning Academy-leksjonen?

Ja. Alle Machine Learning Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Forhåndstrente modeller i torchvision: ResNet, EfficientNet og ViT
  2. Egenskapsuttrekking: Fryse grunnmodellen
  3. Finjustering: Oppheve frysing og bruke lave læringsrater
  4. Domenetilpasning: Medisinsk avbildning med få etiketter
← Tilbake til Machine Learning Academy