Machine Learning
AI-vintern under det sena 80-talet och tidiga 90-talet var en tid av desillusion (bitter känsla som orsakas av att förväntningar eller förhoppningar krossats). Den symboliska AI:ns dröm, att bygga intelligens genom att förprogrammera logiska regler, hade kört fast i den kombinatoriska explosionens och det sunda förnuftets mur. Finansiering för forskning inom AI blev mindre och mindre.
Men under ytan, i skuggan av de misslyckade expertsystemen, pågick en tyst revolution. Istället för att försöka mata en maskin med färdiga regler (rationalism), vad skulle hända om man lät maskinen lära sig reglerna själv genom att titta på data (empirism)? Detta paradigmskifte, från att programmera kunskap till att träna den, markerade återkomsten för John Lockes "oskrivna tavla" och födelsen av det vi idag kallar Maskininlärning (Machine Learning, ML).
Connectionism
Idén om att bygga en tänkande maskin genom att härma hjärnan var inte helt ny. Redan 1943 hade Warren McCulloch och Walter Pitts skapat en första matematisk modell av en biologisk neuron. De visade att denna enkla modell kunde fungera som en logisk grind (som AND, OR, NOT), de grundläggande byggstenarna i en dator. Detta lade grunden för fältet som kom att kallas connectionism – tanken att intelligens inte uppstår ur en enda komplex instruktion, utan ur de sammankopplade nätverken av många extremt enkla enheter, precis som i en hjärna.
Kanske illusterar de delar av en neuron som liknar det som komma skall?
Men för att förstå hur ett helt nätverk fungerar, måste vi först förstå dess minsta, men viktigaste, komponent.
Den Artificiella Neuronen
Tänk dig en neuron som en enkel beslutsmodell. Dess jobb är att väga samman olika informationskällor (inputs) för att fatta ett ja/nej-beslut (output). Låt oss använda en konkret, tvådimensionell analogi: Ska jag gå på festen?
För att fatta detta beslut har du två grundläggande frågor:
- x1: Är vänner där? (
1för Ja,0för Nej) - x2: Har jag ett prov imorgon? (
1för Ja,0för Nej)
Denna artificiella neuronmodell representerar resonemanget med två justerbara "rattar" för varje fråga, kallade vikter (w1, w2), samt en tredje, oberoende ratt kallad bias (b).
- Vikterna representerar hur viktig varje faktor är. En hög positiv vikt för
w1betyder "det är superviktigt att mina vänner är där". En stor negativ vikt förw2betyder "ett prov är en stark anledning att stanna hemma". Geometriskt bestämmer vikterna lutningen, eller orienteringen, på neuronens beslutsgräns. - Bias representerar neuronens grundinställning. Den fungerar som en justerbar tröskel som representerar din grundläggande vilja att gå på fest (positiv bias) eller stanna hemma (negativ bias). Geometriskt bestämmer biasen positionen på beslutsgränsen; den "knuffar" gränsen bort från origo (nollpunkten), vilket ger den friheten att placeras var som helst.
Modellens beräkning är enkel: Summa = (x1*w1) + (x2*w2) + b.
Geometriskt sett är detta mer än bara en summa; ekvationen Summa = 0 definierar en perfekt rak skiljelinje i ett tvådimensionellt rum. Allt på ena sidan linjen är 'Ja', och allt på andra sidan är 'Nej'.
Beslutet fattas sedan av en aktiveringsfunktion: om Summa > 0, blir output 1 (Gå!). Annars blir den 0 (Stanna hemma!).
illustrera några olika vikter och bias, för att visa hur olika individer har olika inställning till idén av tanken att gå på fest.
En Neuron i Python
Innan vi lär en neuron att lära sig själv, låt oss först bygga själva mekanismen i en Python-klass. Denna Neuron-klass kommer att ha en predict-metod som utför beräkningen. Notera att den inte kan lära sig; istället kommer vi att manuellt ställa in dess vikter och bias för att skapa olika beslutsmodeller.
class Neuron:
"""
Representerar en enskild, statisk neuron som kan göra en prediktion.
Denna version kan INTE lära sig. Dess parametrar måste ställas in manuellt.
"""
def __init__(self, weights, bias):
# Vikter och bias är "hårdkodade" när vi skapar neuronen.
self.weights = weights
self.bias = bias
def predict(self, inputs):
"""
Beräknar den viktade summan och returnerar en prediktion (0 eller 1).
"""
# Formeln vi implementerar: Summa = (x1*w1) + (x2*w2) + ... + b
# Låt oss börja på 0
summation = 0
# Loopa igenom varje input och dess motsvarande vikt.
for i in range(len(self.weights)):
summation += inputs[i] * self.weights[i]
# Lägg till bias
summation += self.bias
# Aktiveringsfunktionen: om summan är positiv, gissa 1, annars 0.
if summation > 0:
return 1
else:
return 0
Vad händer här?
summation = 0: Beräkningen av summan börjar alltid på noll.for i in range(len(self.weights)):: Detta är vårfor-loop som ersätter matrisberäkning. Den loopar igenom varje input...summation += inputs[i] * self.weights[i]: ...och multiplicerar den med dess motsvarande vikt, och lägger till resultatet i den totala summan.summation += self.bias: Slutligen läggs vårt bias till.if summation > 0:: Detta är vår enkla aktiveringsfunktion. Om den totala summan är positiv, "aktiveras" neuronen och returnerar1. Annars returnerar den0.
Låt oss nu skapa två olika "personligheter" genom att skapa instanser av vår Neuron-klass med olika parametrar.
Persona 1: Den Studiemotiverade Denna person prioriterar studier över allt annat. Vänner är trevligt, men ett prov är en deal-breaker.
# Skapa en instans av neuronen för den studiemotiverade.
# Parametrar: weights=[vänner, prov], bias
studious_neuron = Neuron(weights=[0.5, -1.0], bias=-0.2)
# Scenario: Vänner är där (1), men det är ett prov imorgon (1)
inputs = [1, 1]
decision = studious_neuron.predict(inputs)
print(f"Den Studiemotiverade: Input {inputs} -> Beslut: {decision} (Förväntat: 0)")
# Beräkning: (1*0.5) + (1*-1.0) + (-0.2) = -0.7. Resultat: 0 (Stanna hemma)
Persona 2: Sociala Festprissen Denna person älskar att umgås och har en mycket mer avslappnad inställning till prov.
# Skapa en instans för festprissen med andra parametrar.
social_neuron = Neuron(weights=[1.0, -0.1], bias=0.5)
# Samma scenario: Vänner är där (1), prov imorgon (1)
inputs = [1, 1]
decision = social_neuron.predict(inputs)
print(f"Sociala Festprissen: Input {inputs} -> Beslut: {decision} (Förväntat: 1)")
# Beräkning: (1*1.0) + (1*-0.1) + 0.5 = 1.4. Resultat: 1 (Gå på fest!)
Detta visar tydligt hur vikter och bias direkt formar neuronens beslutsfattande. Klassen Neuron och dess predict-metod representerar den grundläggande beräkningsmekanismen.
Men vad händer om vi har 100 inputs istället för 2? Att manuellt hitta de bästa värdena för 100 vikter och en bias blir en omöjlig uppgift. Vi behöver ett sätt för neuronen att hitta de bästa parametrarna själv genom att titta på exempel.
Perceptronen
På 50-talet tog Frank Rosenblatt neuronmodellen och gav den en genial och enkel läranderegel. Kombinationen av neuronmodellen och denna regel är vad som kallas Perceptronen. Regeln låter den lära sig från sina misstag genom en trestegsprocess.
Lärandet behöver en uppsättning träningsdata, vilket innehållet exempel på inputs med motsvarande output - alltså facit.
Perceptronens lärande
För varje exempel i träningsdatan, upprepar Perceptronen följande tre steg:
- Gissa: Den tar emot inputs (
x1,x2, ...) och beräknar en output (0eller1) med hjälp av sina nuvarande vikter och bias. - Beräkna Felet: Den jämför sin gissning med det korrekta svaret (facit). Felet beräknas enkelt:
Fel = facit - gissning. Detta kan bara resultera i tre möjliga värden:0: Gissningen var korrekt.1: Gissningen var0men borde ha varit1.-1: Gissningen var1men borde ha varit0.
- Justera Parametrarna (Lärdom): Om felet var
0görs ingenting. Om felet var1eller-1, justeras alla parametrar för att göra gissningen lite bättre nästa gång. Justeringen följer en enkel formel:ny_vikt = gammal_vikt + α * fel * inputny_bias = gammal_bias + α * fel
Här är α (alfa) en inlärningsfaktor (ofta ett litet tal som 0.1), som styr hur stora steg lärandet ska ta.
Genom att upprepa dessa tre steg för många exempel, kommer neuronens vikter och bias gradvis att närma sig värden som löser problemet. Låt oss nu se detta i praktiken.
Scenario 1:
Du står inför ett beslut. Vännerna är på festen (x1=1) och du har ett prov imorgon (x2=1). Det korrekta beslutet för dig är att stanna hemma och plugga (facit = 0).
Din Perceptron har precis startat och har slumpmässiga startvärden:
w1(vänner) =0.5w2(prov) =-0.4b(bias) =0.0
1. Gissning:
Perceptronen räknar:
Summa = (1 * 0.5) + (1 * -0.4) + 0.0 = 0.5 - 0.4 = 0.1
Eftersom 0.1 > 0, blir output 1 (Gå på festen).
2. Misstag:
Gissningen (1) stämmer inte med facit (0). Du borde ha stannat hemma! Felet är facit - gissning = 0 - 1 = -1.
3. Lärdom:
Eftersom felet är negativt, var summan för hög. Perceptronen måste justera sina parametrar för att sänka summan nästa gång den ser en liknande situation. Den använder en enkel uppdateringsregel: ny_parameter = gammal_parameter + inlärningsfaktor * fel * input. Vi sätter inlärningsfaktorn α till 0.1.
w1_ny = 0.5 + 0.1 * (-1) * 1 = 0.4(Vikten för "vänner" minskar lite)w2_ny = -0.4 + 0.1 * (-1) * 1 = -0.5(Vikten för "prov" blir mer negativ)b_ny = 0.0 + 0.1 * (-1) = -0.1(Biasen justeras också)
Med de nya parametrarna (w1=0.4, w2=-0.5, b=-0.1), låt oss testa igen:
Summa = (1 * 0.4) + (1 * -0.5) - 0.1 = 0.4 - 0.5 - 0.1 = -0.2
Nu är summan negativ, och outputen blir 0. Perceptronen har lärt sig!
Scenario 2: Tänk dig en ny situation. Du är en social person vars grundinställning är att man alltid går på fest om man inte har någon information alls.
- Situation: Du vet inget om festen. Inga vänner har sagt något (
x1=0) och du har inget prov (x2=0). - Facit: Din grundinställning säger "Gå!" (facit =
1).
Vi använder de senast inlärda vikterna & bias: w1=0.4, w2=-0.5, b=-0.1.
1. Gissning:
Summa = (0 * 0.4) + (0 * -0.5) + (-0.1) = -0.1
Eftersom summan inte är större än 0, blir output 0 (Stanna hemma).
2. Misstag:
Gissningen (0) stämmer inte med facit (1). Felet är 1 - 0 = 1.
3. Försök till lärdom: Perceptronen justerar nu sina vikter & bias för att höja summan.
w1_ny = 0.4 + 0.1 * (1) * 0 = 0.4(Ingen ändring!)w2_ny = -0.5 + 0.1 * (1) * 0 = -0.5(Ingen ändring!)b_ny = -0.1 + 0.1 * (1) = 0.0(Biasen justeras)
Här ser vi varför bias är så viktigt! Biasen är inte beroende av någon input, så den kan alltid justeras om perceptronen beräknat fel svar.
Detta visar hur vikter och bias har två olika men lika viktiga jobb, både mekaniskt och geometriskt.
- Vikterna lär sig mönster baserat på den input som finns. Geometriskt justerar de orienteringen (lutningen) på beslutsgränsen.
- Biasen lär sig vad neuronens standard-svar ska vara. Geometriskt frigör den beslutsgränsen från origo, vilket låter den justera sin position för att bäst separera datan.
Utan en justerbar bias är neuronens beslutsgräns permanent fastlåst vid nollpunkten och kan bara rotera, vilket gör den oförmögen att lösa många problem.
illustrera en del olika koordinatsystem som visar skiljelinjers lutning och rotation baserat på vikter och bias.
En Perceptron i Python
Nu när vi förstår teorin är det dags att omsätta den i praktiken. Vi kommer att bygga vår egen Perceptron i Python. För att göra det så tydligt som möjligt bygger vi upp den bit för bit och förklarar varje del.
Vi börjar med att definiera en "mall" eller "ritning" för vår Perceptron med hjälp av en Python-klass.
1. Ritningen: __init__ metoden
Först skapar vi själva klassen och dess "konstruktor", __init__. Tänk på __init__ som en funktion som körs automatiskt varje gång vi skapar en ny Perceptron. Dess jobb är att ställa in de grundläggande inställningarna.
# perceptron.py
# importera nödvändiga bibliotek som t.ex. random, numpy, sklearn
import random
class Perceptron:
"""
En enkel Perceptron-implementation.
"""
def __init__(self, learning_rate=0.1, n_iterations=10):
self.learning_rate = learning_rate # Detta är α (alpha) i vår formel
self.n_iterations = n_iterations # Antal gånger vi ska gå igenom träningsdatan
self.weights = None # Vikterna, vi vet inte hur många de är än
self.bias = None # Bias, startar också som okänd
Vad händer här?
class Perceptron:: Vi definierar vår nya ritning.__init__(self, ...): Detta är konstruktorn.learning_rate: Detta är vår inlärningsfaktor (α). Den styr hur stora justeringar vi gör när vi hittar ett fel. Vi ger den ett standardvärde på0.1.n_iterations: Detta tal bestämmer hur många gånger hela träningsdatan ska gås igenom. Att se samma data flera gånger hjälper Perceptronen att finslipa sina vikter.
self.weights = Noneochself.bias = None: När vi skapar Perceptronen vet den ännu ingenting om datan den ska träna på. Därför vet den inte hur många vikter den behöver (en för varje input-feature) eller vad dess bias ska vara. Vi sätter dem tillNoneför att visa att de är tomma från början.
2. Gissningen: predict metoden (Steg 1)
Den första delen av algoritmen: att göra en gissning, är identisk från Neuronen vi tidigare skapade.
3. Lärandet: fit metoden (Steg 2 & 3)
Detta är hjärtat i vår Perceptron. fit-metoden är där själva lärandet sker. Den tar emot träningsdatan (training_examples och training_solutions) och kör lärandeloopen för att justera vikter och bias.
# (Inuti Perceptron-klassen)
def fit(self, training_examples, training_solutions):
"""
Tränar Perceptronen genom att iterera över träningsdatan.
"""
# Steg 0: Initialisering
# Nu vet vi hur datan ser ut. Vi kan skapa vikterna och bias.
# Kolla på första exemplet, hur många inputs finns i varje exempel?
n_inputs = len(training_examples[0])
# Ge varje vikt ett litet slumpmässigt värde, t.ex. mellan -0.5 och 0.5
self.weights = [random.uniform(-0.5, 0.5) for _ in range(n_inputs)]
# Vi kan också slumpa biasen, eller börja den på noll. Båda är vanliga.
self.bias = random.uniform(-0.5, 0.5)
# Huvudloopen för lärande
for i in range(self.n_iterations):
# Gå igenom varje exempel och dess facit
for inputs, solution in zip(training_examples, training_solutions):
# STEG 1: Gissa (använd metoden vi nyss skapade)
prediction = self.predict(inputs)
# STEG 2: Beräkna Felet
# error = facit - gissning
error = solution - prediction
# STEG 3: Justera Parametrar (endast om gissningen var fel)
if error != 0:
# Justera bias: ny_bias = gammal_bias + α * fel
self.bias += self.learning_rate * error
# Justera varje vikt: ny_vikt = gammal_vikt + α * fel * input
for j in range(len(self.weights)):
self.weights[j] += self.learning_rate * error * inputs[j]
Vad händer här, steg för steg?
- Initialisering: Innan loopen startar, tar vi reda på hur många features varje exempel har (
n_features) och skapar en lista med lika många vikter, alla satta till0.0. Vi sätter ocksåbiastill0.0. Detta är vår startpunkt. - Yttre Loop (
for i in range...): Denna loop ser till att vi går igenom hela träningsdatann_iterationsantal gånger. - Inre Loop (
for inputs, solution in zip...): Denna loop går igenom varje enskilt tränings-exempel.zipär en praktisk Python-funktion som parar ihop varjeinputsmed dess motsvarandesolution. - Steg 1, 2 och 3: Inuti den inre loopen utför vi exakt de tre stegen från vår teori:
- Vi gör en
predictionmed den nuvarande kunskapen. - Vi beräknar ett
errorgenom att jämföra med facit (solution). - Om
errorinte är noll, använder vi våra justeringsformler för att uppdatera bådebiasoch allaweights. Notera hur formelnny_vikt = gammal_vikt + α * fel * inputär direkt översatt till kodenself.weights[j] += self.learning_rate * error * inputs[j].
- Vi gör en
Nu har vi alla delar! När vi sätter ihop dem får vi vår kompletta, fungerande Perceptron-klass som är redo att börja lära sig.
Låt oss testa perceptronen i följande tre steg:
- Först testar vi den på vårt enkla "fest-scenario" för att bekräfta att den fungerar som förväntat.
- Sedan tar vi steget till ett klassiskt, verkligt dataset (Iris) för att se hur den hanterar riktig data.
- Slutligen jämför vi vår hemmabyggda Perceptron med den professionella versionen som finns i ett av de mest populära maskininlärningsbiblioteken, Scikit-learn.
Steg 1: Test på vårt "fest-scenario"
Låt oss börja med att applicera vår nyskapade Perceptron på det problem vi känner till bäst. Målet är att den ska lära sig logiken: "gå på fest, om du inte har ett prov".
# --- Användning av vår Perceptron ---
# 1. Definiera vår träningsdata med beskrivande namn.
# 'training_examples' innehåller inputs [[vänner, prov], ...]
training_examples = [
[0, 0], # Inga vänner, inget prov
[0, 1], # Inga vänner, prov
[1, 0], # Vänner, inget prov
[1, 1] # Vänner, prov
]
# 'training_solutions' innehåller motsvarande facit.
training_solutions = [1, 0, 1, 0] # 1 = Gå, 0 = Stanna hemma
# 2. Skapa en instans av vår Perceptron.
perceptron = Perceptron(learning_rate=0.1, n_iterations=5)
# 3. Kör träningen.
# Vi matar in exemplen och facit i vår .fit()-metod.
perceptron.fit(training_examples, training_solutions)
# 4. Granska resultatet.
print(f"\nSlutgiltiga inlärda vikter: {perceptron.weights}")
print(f"Slutgiltig inlärd bias: {perceptron.bias}")
# 5. Testa den färdigtränade modellen.
print("\n--- Testar den tränade modellen ---")
test_fall_1 = [1, 1] # Vänner är där, prov imorgon
test_fall_2 = [1, 0] # Vänner är där, inget prov
print(f"Inputs: {test_fall_1} -> Prediktion: {perceptron.predict(test_fall_1)} (Förväntat: 0)")
print(f"Inputs: {test_fall_2} -> Prediktion: {perceptron.predict(test_fall_2)} (Förväntat: 1)")
Analys av resultatet:
När du kör koden kommer du se hur vikterna och biasen justeras för varje iteration. Det slutgiltiga resultatet kommer troligen att visa:
- En negativ vikt för den andra inputen (
w2, provet). Detta betyder att ett prov starkt talar emot att gå på fest. - En vikt nära noll för den första inputen (
w1, vänner). Modellen har lärt sig att denna faktor inte är avgörande för beslutet i detta specifika dataset. - En positiv bias. Detta representerar en grundinställning att "gå på fest" om inga andra starka skäl (som ett prov) finns.
Modellen har alltså framgångsrikt lärt sig den logiska regeln från datan!
Steg 2: Perceptronen på ett riktigt dataset (Iris)
Nu tar vi steget från ett påhittat problem till ett verkligt. Vi ska använda det berömda Iris-datasetet, som innehåller mätningar av tre olika arter av Iris-blommor.
En viktig begränsning med vår enkla Perceptron är att den är en binär klassificerare, vilket betyder att den bara kan svara på frågor med två möjliga utfall (som 0 eller 1, Ja eller Nej). Iris-datasetet har tre arter, så vi måste förenkla problemet: "Är denna blomma en Iris-Setosa, eller inte?"
Vi kommer också bara att använda två av de fyra tillgängliga måtten (features/inputs) för att hålla det enkelt.
# Vi behöver ladda in datasetet, vilket vi gör med hjälp av biblioteket Scikit-learn.
# Vi behöver ladda in datasetet och nya verktyg
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 1. Ladda och förbered datan
iris = load_iris()
# Välj alla rader, och välj första 2 kolumnerna i varje rad.
# de features eller inputs vi tränar på för varje blomma är sepal length och width
X = iris.data[:, :2]
# skapa facit, 1 om target == 0, annars 0
# target är 0,1,2 för setosa, versicolor, verginica
y = (iris.target == 0).astype(int)
# 2. DELA UPP DATAN
# Vi delar upp X och y i tränings- och test-set.
# test_size=0.3 betyder att 30% av datan blir testdata.
# random_state=1337 säkerställer att vi får samma "slumpmässiga" uppdelning varje gång vi kör koden.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1337)
print(f"Storlek på träningsdata: {len(X_train)} exempel")
print(f"Storlek på testdata: {len(X_test)} exempel\n")
# 3. Skapa och träna en Perceptron-instans PÅ TRÄNINGSDATAN
iris_perceptron = Perceptron(learning_rate=0.1, n_iterations=10)
iris_perceptron.fit(X_train, y_train) # Tränar bara på _train
# 4. Gör prediktioner och utvärdera PÅ TESTDATAN
predictions = [iris_perceptron.predict(inputs) for inputs in X_test]
accuracy = accuracy_score(y_test, predictions) # Jämför med facit för _test
print(f"\n--- Test på Iris-datasetet (med Train/Test Split) ---")
print(f"Noggrannhet på testdatan: {accuracy * 100:.2f}%")
Analys av resultatet:
Du kommer att se att vår enkla Perceptron uppnår mycket imponerande noggrannhet på detta problem! Detta beror på att Iris Setosa-blommorna är så pass olika de andra två arterna (baserat på dessa två features) att det går att dra en perfekt rak linje mellan dem. Detta kallas att datan är linjärt separerbar, vilket är det ideala scenariot för en Perceptron.
Steg 3: Jämförelse med Scikit-learn
Vi har byggt vår Perceptron för att förstå hur den fungerar. I praktiken använder man nästan alltid färdiga, optimerade och robusta implementationer från bibliotek som Scikit-learn. Låt oss se hur man löser exakt samma problem med deras version.
Lägg märke till hur lika kodstrukturen är! Metoderna heter också .fit() och .predict(), vilket är en standard som Scikit-learn använder för alla sina modeller.
# Importera den professionella Perceptron-modellen från Scikit-learn
from sklearn.linear_model import Perceptron as SklearnPerceptron
# 1. Skapa en instans av Scikit-learns Perceptron
# Notera: Vi behöver inte ange learning_rate eller n_iterations,
# den har inbyggda standardvärden och smartare sätt att avgöra när den är färdigtränad.
sk_perceptron = SklearnPerceptron()
# 2. Träna modellen (ser identiskt ut!)
sk_perceptron.fit(X, y)
# 3. Gör prediktioner och utvärdera
sk_predictions = sk_perceptron.predict(X)
sk_accuracy = accuracy_score(y, sk_predictions)
print(f"\n--- Test med Scikit-learns Perceptron ---")
print(f"Noggrannhet med Scikit-learns Perceptron: {sk_accuracy * 100:.2f}%")
Slutsats av jämförelsen:
Resultatet är detsamma (100% noggrannhet), men koden för att använda den färdiga modellen är kortare och den underliggande implementationen är mycket mer effektiv och full av extra funktioner.
Genom att först bygga vår egen Perceptron har vi fått en djup förståelse för vad som händer när vi anropar sk_perceptron.fit(X, y). Vi förstår nu logiken med vikter, bias, inlärningsfaktor och iterationer som ligger gömd bakom den enkla funktionen. Detta är den sanna styrkan med att bygga saker från grunden: det avmystifierar verktygen som experterna använder varje dag.
Perceptronens svaghet
Optimismen var enorm. Men den krossades 1969 när AI-pionjärerna Marvin Minsky och Seymour Papert publicerade boken "Perceptrons". I den bevisade de matematiskt att en enskild Perceptron hade en fundamental, inbyggd begränsning.
Problemet är att en enskild neuron, med sin beräkning (w1*x1) + (w2*x2) + ... + b, är matematiskt begränsad till att skapa en linjär besutsgräns. I vårt tvådimensionella problem är denna gräns en perfekt rak linje. I ett tredimensionellt problem är det ett platt plan. I N dimensioner är det ett (N-1)-dimensionellt "hyperplan".
Lärningsprocessen handlar om att hitta den bästa lutningen (vikterna) och positionen (biasen) för denna linje, men den kan aldrig förvandla linjen till en kurva, en cirkel eller någon annan form.
Vad händer med ett lite mer komplext beslut?
Låt oss bygga vidare på vårt "Ska jag gå på festen?"-exempel. Dina två inputs är nu:
- x1: Är Ron på festen? (
1för Ja,0för Nej) - x2: Är Hermione på festen? (
1för Ja,0för Nej)
Din beslutsregel är lite komplicerad. Du vill gå om en av dem är där, men du vill inte gå om ingen är där (tråkigt) och du vill absolut inte gå om båda är där (för att undvika att bli ett femte hjul).
Detta är ett klassiskt logiskt problem som kallas XOR ("antingen eller, men inte båda"). Låt oss översätta din regel till siffror, där 1 är "Gå!" och 0 är "Stanna hemma!":
- Ron är INTE där (
0) och Hermione är INTE där (0) → Stanna hemma (0). - Ron ÄR där (
1) och Hermione är INTE där (0) → Gå! (1). - Ron är INTE där (
0) och Hermione ÄR där (1) → Gå! (1). - Ron ÄR där (
1) och Hermione ÄR där (1) → Stanna hemma (0).
Låt oss plotta dessa möjliga utfall på en 2D-graf. Rons närvaro är x-axeln och Hermiones närvaro är y-axeln. Vi använder en grön cirkel för "Gå!" och ett rött kryss för "Stanna hemma!".
Du får då fyra punkter:
- En punkt vid (0, 0) som är ett rött kryss.
- En punkt vid (1, 0) som är en grön cirkel.
- En punkt vid (0, 1) som är en grön cirkel.
- En punkt vid (1, 1) som är ett rött kryss.
Den omöjliga linjen
Utmaningen är nu: Kan en Perceptron hitta en uppsättning vikter w1, w2 (linjens lutning) och en bias b (linjens position) så att linjen w1*x1 + w2*x2 + b = 0 perfekt separerar de gröna cirklarna från de röda kryssen?
Illustration: Fest-dilemmat (XOR-problemet). Rita en 2D-graf med axlarna "Är Ron där?" och "Är Hermione där?", där varje axel bara har punkterna 0 och 1.
- Rita ett rött kryss vid koordinaten (0, 0) och märk det "Stanna hemma".
- Rita en grön cirkel vid koordinaten (1, 0) och märk det "Gå!".
- Rita en grön cirkel vid koordinaten (0, 1) och märk det "Gå!".
- Rita ett rött kryss vid koordinaten (1, 1) och märk det "Stanna hemma".
- Rita en streckad linje för att visa ett misslyckat försök att separera cirklarna från kryssen. Skriv en bildtext: "Det är omöjligt att separera de tillfällen man vill gå från de man vill stanna hemma med en enda rak linje."
Titta på grafen. Det är geometriskt omöjligt.
- En horisontell linje? Separerar inte.
- En vertikal linje? Separerar inte.
- En diagonal linje? Den kommer alltid att ha en grön cirkel och ett rött kryss på fel sida.
Detta var Minsky och Paperts bevis. De visade att eftersom en Perceptron bara kan lära sig linjära separationer, kunde den inte ens lösa detta grundläggande sociala dilemma. Perceptronens matematiska verktyg – den raka linjen – var helt enkelt inte tillräckligt sofistikerat för problemet. Slutsatsen de drog: om denna modell inte ens klarar av att bestämma om man ska gå på en fest, hur ska den då någonsin kunna efterlikna den komplexa mänskliga hjärnan?
Det påstås att denna kritik var bidragande till att forskning inom AI koncentrerades till symbolisk AI, vilket som vi redan nämnt ledde oss in i den första "AI-vintern".
Men hallå, chilla gorilla, 2 sekunder bara.
Visserligen är det tydligt att en perceptron inte kan modellera en XOR operation. Men vi kan enkelt modellera NAND eller NOR operationerna.
insert tabell + graf över nand & nor
Något spännande med dessa två är deras unika egenskap att de kan användas för att realisera alla andra logiska funktioner. Det är alltså möjligt att bygga en XOR eller vilken annan logisk funktion som helst med hjälp av endast NAND-grindar eller endast NOR-grindar.
illustrera hur XOR kan representeras som 4stycken NAND grindar (perceptrons som lärt sig NAND)
Så flera perceptroner sammanlänkade kan alltså representera mer avancerade mönster? Coolt!
Och hur var det med riktiga neuroner? Den mänskliga hjärnan som är väldigt komplex består av ett enormt nätverk av sammankopplade neuroner... Känns det som att det kanske ändå fanns ett spår framåt här som förtjänade att utforskas...?
Neurala Nätverk
Några få forskare vägrade ge upp. De insåg att problemet inte var neuronen i sig, utan att man bara använde en. Våra hjärnor har miljarder neuroner. Vad skulle hända om man, precis som i hjärnan, kopplade ihop massor av dem i lager? Detta skapar vad vi kallar ett Neuralt Nätverk.
Ett neuralt nätverk består vanligtvis av tre typer av lager:
- Input-lager: Tar emot den initiala datan. Varje neuron representerar en del av datan (t.ex. en pixel i en bild).
- Dolda lager (Hidden Layers): Ett eller flera mellanliggande lager. Här sker det verkliga "tänkandet". Magin ligger i att varje lager lär sig att känna igen mönster från det föregående lagret, vilket skapar en hierarki av kunskap.
- Output-lager: Producerar det slutgiltiga svaret (t.ex. "Detta är en bild på en katt").
illustrera ett simpelt nätverk
Steg för steg: Hur ett nätverk lär sig känna igen siffran "4"
Låt oss följa datan genom ett enkelt nätverk som ska känna igen handskrivna siffror. Input är en liten bild, 28x28 pixlar, av siffran "4".
Steg 1: Input-lagret
Nätverket har 784 neuroner i sitt input-lager (28 * 28 = 784). Varje neuron tar emot värdet från en enda pixel – kanske 1 för en svart pixel och 0 för en vit. Detta lager "ser" bara en massa osammanhängande punkter.
Illustration: Input-lagret. Visa en bild av en handskriven "4" på ett rutnät. Rita sedan en kolumn av cirklar (neuroner) bredvid, och visa hur varje pixel mappas till en neuron i input-lagret.
Steg 2: Första Dolda Lagret – Hitta enkla former Neuronerna i detta lager får sina inputs från alla neuroner i input-lagret. Genom träning (som vi kommer till) har de specialiserat sig.
- Neuron A i detta lager kanske har lärt sig att "avfyra" starkt när den ser en kort, vertikal linje i det övre vänstra hörnet. Den har utvecklat höga positiva vikter för pixlarna i just det området och låga eller negativa vikter för alla andra.
- Neuron B har lärt sig att känna igen en horisontell linje i mitten.
- Neuron C har lärt sig att känna igen en lång, vertikal linje till höger.
När bilden av en "4" matas in, kommer Neuron A, B och C alla att avfyra starkt, medan en neuron som letar efter en cirkel (som i en "8" eller "0") kommer att vara tyst. Outputen från detta lager är alltså inte längre pixlar, utan en samling koncept: "ja, det finns en vertikal linje här", "ja, det finns en horisontell linje där".
Illustration: Första dolda lagret. Visa bilden av en "4". Rita sedan tre exempelneuroner från det dolda lagret. Från den första, rita linjer till pixlarna i den korta vertikala delen av "4:an" och färglägg dem. Gör likadant för de andra två neuronerna och deras respektive delar av siffran.
Steg 3: Andra Dolda Lagret (eller Output-lagret) – Kombinera formerna Neuronerna i detta lager får sina inputs från neuronerna i det föregående lagret. De lär sig att känna igen kombinationer av de enkla formerna.
- Det finns en neuron i output-lagret för varje möjlig siffra (0-9).
- "Fyra"-neuronen har lärt sig att den ska avfyra om den får starka signaler från de neuroner i föregående lager som känner igen "kort vertikal linje uppe till vänster", "horisontell linje i mitten" OCH "lång vertikal linje till höger". Den har alltså höga positiva vikter kopplade till just Neuron A, B och C.
- "Etta"-neuronen har istället lärt sig att den bara behöver en stark signal från Neuron C (lång vertikal linje).
Eftersom bilden av en "4" aktiverade A, B och C, kommer "Fyra"-neuronen att få en mycket hög summerad input och avfyra med högsta sannolikhet. Nätverkets svar blir: "Jag är 99% säker på att detta är en 4".
Men okej, vänta lite här. 99% ?! starka signaler? Hittills har vi bara diskuterat 1 eller 0 som input/output.
Vår perceptron behöver få några uppgraderingar till.
Idén att kombinera flera enkla enheter för att lösa komplexa problem är själva kärnan i neurala nätverk. Men innan vi börjar bygga nätverk av Perceptroner, finns det en annan fundamental begränsning vi måste adressera hos vår enskilda neuron: dess digitala natur.
Hittills har vår modell levt i en binär värld:
- Input: "Är vänner där?" (
1eller0). - Output: "Gå på festen!" (
1eller0).
Men verkligheten är sällan så svartvit.
Variationer i input
Tänk om vår input inte var en ja/nej-fråga, utan en gradvis skala?
- Istället för "Är vänner där?", kanske frågan är "Hur många vänner är där?" (en siffra som 0, 1, 5, 10...).
- Istället för "Har jag ett prov imorgon?", kanske det är "Hur svårt är provet på en skala 1-10?".
Den goda nyheten är att vår neurons beräkning av vikterna, Summa = (x1*w1) + (x2*w2) + b, hanterar detta utan problem. Vikterna (w) fungerar fortfarande som en justerbar "viktighet" för varje input, oavsett om inputen är 0.75 eller 100. Den matematiska grunden är alltså redan flexibel nog för att hantera icke-binär input.
Variationer i output
Den verkliga flaskhalsen är vår aktiveringsfunktion. Just nu använder vi en enkel stegfunktion: om summan är större än noll blir resultatet 1, annars 0. Det är en "allt eller inget"-mekanism.
Men tänk om vi vill förutsäga något som inte är ett simpelt ja/nej-beslut?
- Vad är sannolikheten för att det kommer att regna imorgon (ett tal mellan 0.0 och 1.0)?
- Vad kommer ett hus att kosta (ett positivt, kontinuerligt värde)?
Här behöver vi ersätta den hårda stegfunktionen med något mjukare. Istället för en tröskel som abrupt slår om från 0 till 1, kan vi använda en funktion som skapar en mjuk, gradvis övergång.
En av de mest klassiska "mjuka" aktiveringsfunktionerna är Sigmoid-funktionen. Den tar vilket tal som helst (från minus oändligheten till plus oändligheten) och "klämmer ihop" det till ett värde mellan 0 och 1.
Illustration: Stegfunktion vs. Sigmoid-funktion. Rita två grafer sida vid sida.
- Vänster graf: Visa den klassiska stegfunktionen. En linje vid y=0 som abrupt hoppar till y=1 vid x=0. Bildtext: "Stegfunktionen: Output är antingen 0 eller 1. Ett hårt, binärt beslut."
- Höger graf: Visa en S-formad kurva (Sigmoid) som mjukt går från nära 0 till nära 1 och passerar 0.5 vid x=0. Bildtext: "Sigmoid-funktionen: Output är ett värde mellan 0 och 1. Perfekt för att representera en sannolikhet."
Denna lilla förändring – att byta ut stegfunktionen mot en mjukare, differentierbar funktion som Sigmoid – är en av de mest avgörande uppgraderingarna av Perceptron-modellen. Det ger neuronen två superkrafter:
- Den kan ge nyanserade svar: Istället för "Ja" kan den säga "87% chans".
- Den ger bättre feedback för lärande: Eftersom funktionen är mjuk, kan vi se hur nära vi var ett korrekt beslut. Om gissningen var
0.6och facit1.0, var felet mindre än om gissningen var0.1. Denna nyans är kritisk för de mer avancerade inlärningsalgoritmer som moderna neurala nätverk använder.
Genom att uppgradera vår enskilda neuron till att hantera kontinuerliga värden och använda en mjuk aktiveringsfunktion, har vi skapat en mer kraftfull och flexibel byggsten.
Coolt! Men alltså... Hur förstog varje perceptron hur den skulle uppdatera sina vikter? Tidigare fick en perceptron jämföra sitt svar med ett facit. Om svaret från output lagret är det som jämförs med facit.. hur kan då enskilda perceptroner i någon av de gömda lagren lära sig? :O
Detta är känt som "The Credit Assignment Problem" (ungefär "problemet med att fördela beröm och skuld"). Om nätverket gissar fel, vilka av de tusentals neuronerna och miljontals kopplingarna bär ansvaret?
Backpropagation
Den geniala lösningen kom 1986 med en teknik kallad Backpropagation ("bakåtpropagering av fel").
Scenario: Nätverket gissar fel Vi matar in bilden av en "4", men nätverkets nuvarande vikter är dåliga, så den neuron som lyser starkast i output-lagret är "9"-neuronen. Den är 90% säker på att det är en nia. "4"-neuronen är bara 5% säker.
Felet är uppenbart. Nu börjar backpropagation-processen, som sker i två steg baklänges genom nätverket:
Steg 1: Skuldfördelning i sista lagret Algoritmen tittar på output-lagret och konstaterar:
- Till "9"-neuronen: "Du skulle ha varit tyst (
0), men du skrek högt (0.9). Ditt fel är stort och för högt. Du var för självsäker." - Till "4"-neuronen: "Du skulle ha skrikit högt (
1), men du viskade bara (0.05). Ditt fel är stort och för lågt. Du var för osäker." - Till de andra neuronerna (0, 1, 2, 3, 5, 6, 7, 8): "Ni var ganska tysta, vilket var rätt. Ert fel är litet."
Steg 2: En kedjereaktion av justeringar bakåt Nu börjar den verkliga magin. Varje felaktig neuron tittar "bakåt" på de kopplingar (vikter) och neuroner i det föregående lagret som gav den sin input.
-
"9"-neuronen resonerar så här: "Jag aktiverades för starkt. Vilka av mina inkommande signaler var starkast? Aha, det var från neuronerna i det dolda lagret som känner igen 'en cirkel högst upp' och 'en lång vertikal linje'. De bidrog mest till mitt misstag. För att jag ska göra mindre fel nästa gång, måste jag lita mindre på dem."
- Åtgärd: Algoritmen sänker vikten på kopplingen mellan "cirkel-neuronen" och "9"-neuronen. Den gör samma sak för "lång-vertikal-linje-neuronen".
-
"4"-neuronen resonerar samtidigt: "Jag aktiverades för svagt. Jag borde ha lyssnat mer på de neuroner som faktiskt signalerade korrekt. Aha, neuronerna för 'kort vertikal linje', 'horisontell linje' och 'lång vertikal linje' var alla aktiva. Deras bidrag var korrekt, men jag ignorerade dem."
- Åtgärd: Algoritmen höjer vikten på kopplingarna från dessa tre ingående neuronerna till "4"-neuronen.
Denna skuldfördelning fortsätter sedan bakåt. "Cirkel-neuronen" (som felaktigt bidrog till en "9:a") får nu själv en "skuldsignal" och resonerar: "Okej, jag aktiverades för starkt. Vilka pixlar i input-bilden var det som fick mig att aktiveras? Jag måste sänka vikterna från dem."
Många bäckar små
Varje enskild justering är pytteliten. Men processen upprepas för tusentals, ibland miljontals, exempelbilder. För varje bild gör nätverket en gissning, beräknar felet och skickar en våg av pyttesmå korrigeringar bakåt genom hela systemet.
Det är denna eleganta, matematiska process av att fördela skuld baklänges och göra små, ständiga justeringar som gör att ett nätverk kan gå från att gissa slumpmässigt till att klassificera bilder med övermänsklig precision. Plötsligt var vägen öppen för att bygga de djupt komplexa och kraftfulla inlärningssystem vi ser idag.
Illustration: Hela nätverket. En översiktsbild som visar:
- Input-lagret (en matris av neuroner som representerar "4:an").
- Pilar till Första Dolda Lagret, där några neuroner är upplysta (de som känner igen kanter/linjer i "4:an").
- Pilar till Output-lagret (10 neuroner, 0-9), där endast neuronen för "4" är starkt upplyst.
- En böjd pil märkt "Backpropagation" som går från output bakåt genom nätverket, för att visa hur felet justerar vikterna.
Träning av Neurala Nätverk
Perceptronens läranderegel var elegant i sin enkelhet, men den fungerar inte för ett nätverk med flera lager. Dels för att våra nya neuroner ger nyanserade svar (t.ex. 0.87 istället för 1), men framför allt på grund av problemet med skuldfördelning: hur vet en neuron i ett dolt lager om den gjorde "rätt" eller "fel"?
För att lösa detta krävs en mer sofistikerad, tvådelad process: Gradient Descent som övergripande strategi för att hitta de bästa vikterna, och Backpropagation som den mekanism som gör strategin möjlig.
Gradient Descent: Att Hitta Dalens Botten
Tänk dig ett landskap där höjden representerar nätverkets kostnad (hur fel det har). Vårt mål är att hitta den lägsta punkten. Detta gör vi genom att ta små steg i den riktning som lutar brantast nedåt. Denna "brantaste lutning" kallas gradient.
Uppdateringsregeln för varje enskild vikt i nätverket är:
ny_vikt = gammal_vikt - α * gradienten_för_denna_vikt
Här är α vår inlärningsfaktor. gradienten_för_denna_vikt är ett tal som säger "exakt hur mycket och åt vilket håll påverkar denna specifika vikt den totala kostnaden?". Att beräkna denna gradient för miljontals vikter, varav många ligger djupt inne i nätverket, är den verkliga utmaningen, som löses med backpropagation - bakåtpropagering.
Matematiken i Backpropagation: En Steg-för-steg-guide
Backpropagation är en algoritm för att beräkna gradienten för varje vikt. Den gör detta genom att först beräkna ett "felansvar" för varje enskild neuron. Detta felansvar kallar vi för delta (δ). När vi vet varje neurons delta, blir det enkelt att räkna ut gradienten för dess inkommande vikter.
Processen för ett enskilt tränings-exempel sker alltid i fyra steg:
Steg 1: Framåtpasset (Prediction) Först skickar vi in vår data och låter den flöda framåt genom nätverket, lager för lager, tills vi får en slutgiltig gissning. Under detta steg är det avgörande att vi sparar alla outputs från varje enskild neuron. Vi kommer att behöva dem i de kommande stegen.
- I koden: Detta motsvarar den första delen av
.fit()-metoden, där vi fyller listanoutputs_by_layer.
Steg 2: Felansvar (Delta) för Output-lagret
Nu börjar vi bakifrån. För varje neuron i det allra sista lagret kan vi direkt beräkna dess felansvar (delta). Formeln är:
δ_output = (facit - gissning) * derivatan_av_aktiveringsfunktionen(gissning)
Låt oss bryta ner det:
(facit - gissning): Detta är det uppenbara felet. Om gissningen var 0.7 och facit 1.0, är felet 0.3.derivatan_av_aktiveringsfunktionen(...): Detta är en "skalningsfaktor" från kedjeregeln. Den svarar på frågan: "Hur känslig var neuronens output för ändringar i sin summerade input?". Om neuronen gav en output på 0.99 är den nästan "mättad" och dess aktiveringsfunktion är väldigt platt. Då blir derivatan nära noll, vilket gördeltalitet – neuronen hade inte kunnat ändra sig så mycket ändå.- I koden: Detta är den första
for-loopen i backpropagation-delen, där vi beräknaroutput_deltas.errori koden är(facit - gissning).
Steg 3: Felansvar (Delta) för Dolda Lager
Detta är kärnan i backpropagation. För att beräkna delta för en neuron i ett dolt lager, tittar vi "framåt" på de deltas vi just beräknade i nästa lager. Formeln är:
δ_dold = (summerat fel från nästa lager) * derivatan_av_aktiveringsfunktionen(dold_neurons_output)
Låt oss bryta ner den första, viktigaste delen:
(summerat fel från nästa lager): För att beräkna detta, frågar vår dolda neuron varje neuron i lagret framför: "Vad var ditt delta, och hur stark var kopplingen (vikten) mellan oss?". Den multiplicerar dessa två värden för varje koppling framåt och summerar resultatet. Detta är en viktad summa av felansvaret från nästa lager.- I koden: Detta motsvarar den näst sista
for-loopen. Variabelnerror_contributionär exakt denna summering. Den samlar in skulden från nästa lager, viktat av styrkan på kopplingarna.
Steg 4: Beräkna Justeringar och Uppdatera Vikter & Biaser
Nu när vi har delta (felansvaret) för varje neuron i hela nätverket, är det sista steget att beräkna hur mycket varje parameter ska justeras.
Justering av Vikter:
Gradienten (lutningen) för en specifik vikt som går från Neuron A (med output O_A) till Neuron B (med delta δ_B) är:
gradient_för_vikt = δ_B * O_A
Intuitionen är: en vikts justering ska vara som störst om...
- ...neuronen den leder till hade ett stort felansvar (
δ_Bär stort). - ...signalen som passerade genom vikten var stark (
O_Aär stort).
Om signalen var noll, hade vikten inget inflytande och dess justering blir noll, oavsett hur stort felet var.
Justering av Bias: Och hur uppdateras biasen? Det är ännu enklare. Biasen är inte kopplad till någon föregående neurons output. Det bästa sättet att tänka på den är som en vikt som är kopplad till en "fiktiv" input som alltid har värdet 1.
Om vi använder samma formel som för en vanlig vikt blir gradienten för biasen:
gradient_för_bias = δ_B * 1 = δ_B
Gradienten för en neurons bias är alltså helt enkelt neuronens eget delta! Den påverkas inte av vad som kom in i neuronen, bara av neuronens totala felansvar.
Uppdateringsregeln: Nu kan vi använda dessa gradienter för att justera våra parametrar. Justeringen vi gör är:
justering = inlärningsfaktor * gradient
Och vi lägger till denna justering till våra gamla vikter och biaser:
ny_vikt = gammal_vikt + α * (δ_B * O_A)
ny_bias = gammal_bias + α * δ_B
- I koden: Detta är den allra sista, nästlade
for-loopen.deltaärδ_B.input_val(ellerinputs_to_layer[k]) ärO_A.- Raden
neuron.weights[k] += learning_rate * delta * input_valimplementerar exakt uppdateringen för vikten. - Raden
neuron.bias += learning_rate * deltaimplementerar den enklare uppdateringen för biasen.
Genom att upprepa dessa fyra steg tusentals gånger, justeras alla vikter och biaser i nätverket gradvis för att minimera den totala kostnaden.
Ett Neuralt Nätverk i Python
Nu när vi har en detaljerad förståelse för algoritmen, blir koden en direkt översättning av dessa steg.
Byggstenen: En Passiv Neuron
import numpy as np
import math
class Neuron:
"""En passiv neuron som bara kan göra en prediktion."""
def __init__(self, num_inputs):
# Ge varje vikt ett litet slumpmässigt värde, t.ex. mellan -0.5 och 0.5
self.weights = [random.uniform(-0.5, 0.5) for _ in range(n_inputs)]
# Vi kan också slumpa biasen, eller börja den på noll. Båda är vanliga.
self.bias = random.uniform(-0.5, 0.5)
def _sigmoid(self, x):
if x < -700: return 0.0
return 1 / (1 + math.exp(-x))
def _sigmoid_derivative(self, sig_output):
return sig_output * (1 - sig_output)
def predict(self, inputs):
"""Beräknar (x1*w1 + x2*w2 + ...) + b"""
total = 0
for i in range(len(self.weights)):
total += inputs[i] * self.weights[i]
total += self.bias
return self._sigmoid(total)
Tränaren: Nätverket
class NeuralNetwork:
"""Ett nätverk som agerar "tränare" för sina neuroner."""
def __init__(self, layer_sizes):
self.layers = []
for i in range(1, len(layer_sizes)):
num_inputs = layer_sizes[i-1]
num_neurons = layer_sizes[i]
self.layers.append([Neuron(num_inputs) for _ in range(num_neurons)])
def predict(self, inputs):
current_inputs = inputs
for layer in self.layers:
next_inputs = [neuron.predict(current_inputs) for neuron in layer]
current_inputs = next_inputs
return current_inputs
def fit(self, training_examples, training_solutions, epochs, learning_rate):
for epoch in range(epochs):
for inputs, solution in zip(training_examples, training_solutions):
# STEG 1: FRAMÅTPASS & SPARA OUTPUTS
outputs_by_layer = [inputs]
current_inputs = inputs
for layer in self.layers:
next_inputs = [n.predict(current_inputs) for n in layer]
outputs_by_layer.append(next_inputs)
current_inputs = next_inputs
deltas = []
# STEG 2: BERÄKNA DELTA FÖR OUTPUT-LAGRET
final_outputs = outputs_by_layer[-1]
output_deltas = []
for i, neuron in enumerate(self.layers[-1]):
error = solution[i] - final_outputs[i]
delta = error * neuron._sigmoid_derivative(final_outputs[i])
output_deltas.append(delta)
deltas.append(output_deltas)
# STEG 3: BERÄKNA DELTA FÖR DOLDA LAGER (BAKLÄNGES)
for i in range(len(self.layers) - 2, -1, -1):
current_deltas = []
for j, neuron in enumerate(self.layers[i]):
error_contribution = 0
for k, next_neuron in enumerate(self.layers[i+1]):
error_contribution += deltas[0][k] * next_neuron.weights[j]
neuron_output = outputs_by_layer[i+1][j]
delta = error_contribution * neuron._sigmoid_derivative(neuron_output)
current_deltas.append(delta)
deltas.insert(0, current_deltas)
# STEG 4: UPPDATERA VIKTER OCH BIASER
for i, layer in enumerate(self.layers):
inputs_to_layer = outputs_by_layer[i]
for j, neuron in enumerate(layer):
delta = deltas[i][j]
for k, input_val in enumerate(inputs_to_layer):
gradient = delta * input_val
neuron.weights[k] += learning_rate * gradient
neuron.bias += learning_rate * delta
if (epoch % 1000) == 0:
predictions = [self.predict(x) for x in training_examples]
error = ((np.array(training_solutions) - np.array(predictions).reshape(len(training_solutions),-1))**2).mean()
print(f"Epoch {epoch}, Medelfel: {error:.4f}")
Lös XOR – Ett Problem för ett Nätverk
# --- Träningsdata för XOR ---
X_train = [[0, 0], [0, 1], [1, 0], [1, 1]]
y_train = [[0], [1], [1], [0]]
nn = NeuralNetwork(layer_sizes=[2, 2, 1])
print("Startar träning av neuralt nätverk för XOR...")
nn.fit(X_train, y_train, epochs=10000, learning_rate=0.1)
print("Träning klar!")
print("\nTestresultat:")
for inputs, solution in zip(X_train, y_train):
prediction = nn.predict(inputs)
print(f"Input: {inputs} -> Gissning: {prediction[0]:.4f}, Rätt svar: {solution[0]}")
När koden körs ser vi hur medelfelet stadigt minskar, ett bevis på att nätverket lär sig. Slutresultatet visar gissningar som är mycket nära de korrekta svaren (0 eller 1), vilket demonstrerar att nätverket har lyckats lära sig den icke-linjära relationen i XOR-data – en bedrift som krävde samverkan mellan flera neuroner och den sofistikerade träningsprocessen med Gradient Descent och Backpropagation.
De Två Stora Familjerna av ML
Klassisk maskininlärning delas oftast in i två huvudkategorier, baserat på vilken typ av data man har och vad man vill uppnå.
Supervised Learning (Övervakad Inlärning)
Detta är den vanligaste formen av maskininlärning. "Övervakad" betyder att vi tränar modellen på ett dataset där vi redan känner till de korrekta svaren. Vi har en "facitlista". Målet är att modellen ska lära sig den underliggande funktionen som kopplar ihop en input med en korrekt output, så att den sedan kan göra korrekta förutsägelser på ny, osedd data.
Inom övervakad inlärning finns två huvudtyper av problem:
-
Klassificering: Målet är att förutsäga en kategorisk etikett. Du sorterar data i fack.
- Exempel: Spam-filtrering. Modellen matas med tusentals e-postmeddelanden som manuellt har märkts som antingen "spam" eller "inte spam". Modellen lär sig att känna igen mönster (vissa ord, avsändare, länkar) som är typiska för skräppost. När ett nytt, okänt mejl kommer in, kan modellen klassificera det korrekt.
- Exempel: Bildigenkänning. Modellen tränas på miljontals bilder som är märkta med vad de föreställer ("katt", "hund", "bil"). Den lär sig känna igen visuella mönster av pixlar som utgör en katt, och kan sedan identifiera katter i helt nya bilder.
-
Regression: Målet är att förutsäga ett kontinuerligt, numeriskt värde.
- Exempel: Huspriser. Modellen matas med data om tusentals sålda hus, där varje hus har egenskaper (input: antal rum, yta, läge) och ett känt slutpris (output). Modellen lär sig hur dessa egenskaper viktas för att förutsäga priset på ett nytt hus som kommer ut på marknaden.
- Exempel: Försäljningsprognoser. Ett företag matar en modell med historisk försäljningsdata, information om väder, helgdagar och reklamkampanjer. Modellen lär sig sambanden och kan sedan förutsäga hur mycket glass de kommer att sälja nästa vecka.

En fundamental princip inom övervakad inlärning är uppdelningen i tränings- och testdata. Man tränar alltid modellen på en del av datan (t.ex. 80%) och utvärderar sedan dess prestanda på en separat, "osedd" del (de återstående 20%). Att testa på samma data som man tränat på vore som att ge en student exakt samma frågor på provet som de haft på sina övningsuppgifter – det mäter bara förmågan att memorera, inte att generalisera och faktiskt "kunna" ämnet.
Målet med all maskininlärning är generalisering, inte memorering. En bra modell är inte en som kan rabbla upp exakta svar från träningsdatan, utan en som har lärt sig de underliggande mönstren så väl att den kan göra korrekta förutsägelser på helt ny och osedd data.
Fallstudie 1: Linjär Regression
Detta är ett av de enklaste exemplen på regression. Målet är att dra en rak linje som bäst passar en samling datapunkter, t.ex. för att förutsäga huspriser baserat på boyta.
- Modellen: En enkel linje,
y = mx + c, därm(lutning) ochc(skärningspunkt) är modellens "vikter". - Kostnadsfunktionen: Man använder Mean Squared Error (MSE), som beräknar det genomsnittliga kvadratiska avståndet från varje datapunkt till linjen. Målet är att hitta den linje som minimerar detta totala avstånd.
- Lärandet: Modellen använder Gradient Descent för att hitta de optimala värdena på
mochc. Den startar med en slumpmässig linje, beräknar felet (MSE), och justerar sedanmochcstegvis i den riktning som minskar felet, tills linjen passar datan så bra som möjligt.
Med biblioteket scikit-learn kan man träna en linjär regressionsmodell på bara några rader kod:
from sklearn.linear_model import LinearRegression
# Ladda din data (X = boyta, y = pris)
model = LinearRegression()
model.fit(X, y)
# Modellen är nu tränad och kan göra förutsägelser
Fallstudie 2: Beslutsträd i Praktiken
Ett beslutsträd är en kraftfull och lättolkad modell för klassificering. Den lär sig en serie av OM-DÅ-regler som delar upp datan i allt renare grupper. Istället för att bara beskriva det, låt oss bygga ett och visualisera det med scikit-learn på vårt välkända Iris-dataset.
- Hur det fungerar: Algoritmen försöker hitta den bästa frågan att ställa för att dela upp datan. Vad är en "bra" fråga? Det är en fråga (t.ex. "Är
petal length< 2.45 cm?") som resulterar i två grupper som är så "rena" som möjligt, dvs. där varje grupp så långt som möjligt bara innehåller en enda klass. För att mäta detta används ett mått som Gini Impurity. Processen upprepas sedan för varje ny grupp, och trädet växer tills det har klassificerat all data.
Det bästa sättet att förstå ett beslutsträd är att se det. Koden nedan tränar en DecisionTreeClassifier och använder sedan matplotlib för att rita upp exakt de regler som modellen har lärt sig.
# decision_tree_iris.py
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, plot_tree
# 1. Ladda data
iris = load_iris()
X, y = iris.data, iris.target
# 2. Skapa och träna modellen
# Vi sätter max_depth=3 för att trädet inte ska bli för stort och svårläst
clf = DecisionTreeClassifier(max_depth=3, random_state=42)
clf.fit(X, y)
# 3. Visualisera trädet
plt.figure(figsize=(20,10))
plot_tree(clf,
filled=True,
rounded=True,
class_names=iris.target_names,
feature_names=iris.feature_names)
plt.title("Beslutsträd tränat på Iris-datasetet")
plt.show()
När du kör koden kommer ett diagram att visas. Varje ruta (nod) i trädet representerar en fråga. Genom att följa frågorna från roten (översta noden) ner till ett löv (en nod utan barn) kan du se exakt hur modellen skulle klassificera en ny blomma. Detta är en av de största fördelarna med beslutsträd: till skillnad från ett neuralt nätverk är de inte en "svart låda". Vi kan inspektera och förstå exakt hur de resonerar.
Unsupervised Learning (Oövervakad Inlärning)
Här har vi den motsatta och ofta mer utmanande situationen. Vi har ett dataset, men vi har ingen aning om vad de "korrekta" svaren är. Vi har ingen facitlista eller fördefinierade etiketter. Målet är istället att låta modellen agera som en digital upptäcktsresande och på egen hand hitta dolda mönster, strukturer och samband i datan. Det handlar inte om att förutsäga ett känt svar, utan om att skapa förståelse ur kaos.
De två vanligaste uppgifterna inom oövervakad inlärning är clustering och dimensionsreducering, och båda löser fundamentala problem när man arbetar med stora datamängder.
1. Clustering (Klustring)
Clustering-algoritmer har som mål att hitta den dolda, naturliga grupperingen i datan. Modellen försöker placera datapunkter som liknar varandra i "kluster" och samtidigt se till att klustren är så olika varandra som möjligt.
- Exempel: Kundsegmentering. En butikskedja matar in anonymiserad köpdata från tusentals kunder (vad de köper, när de handlar, hur mycket de spenderar). Clustering-algoritmen kanske på egen hand identifierar tre distinkta grupper: "studenter som handlar billigt på kvällar", "barnfamiljer som storhandlar på helger" och "pensionärer som köper specifika varor på förmiddagar".
Varför är detta viktigt? Att bara ha en stor mängd kunddata är inte särskilt användbart. Clustering omvandlar denna rådata till värdefull insikt.
- Målinriktad marknadsföring: Istället för att skicka samma reklam till alla, kan företaget nu skicka specifika erbjudanden som är relevanta för varje grupp. Detta ökar försäljningen och kundnöjdheten.
- Upptäcka nya marknader: Kanske hittar algoritmen ett oväntat kluster, t.ex. "personer som bara köper ekologiska produkter sent på tisdagar". Detta kan vara en helt ny nisch att utforska.
- Anomalidetektering: Om en ny datapunkt inte passar in i något av de befintliga klustren kan det vara en avvikelse, t.ex. ett bedrägligt köpbeteende eller ett fel i systemet.
2. Dimensionality Reduction (Dimensionsreducering)
Modern data innehåller ofta hundratals eller tusentals variabler (dimensioner) för varje datapunkt. Tänk dig en patientjournal med data om allt från blodtryck och vikt till hundratals olika genuttryck. Detta skapar två stora problem: det blir omöjligt för en människa att visualisera, och det kan göra maskininlärningsmodeller extremt långsamma och ineffektiva (ett fenomen som kallas "The Curse of Dimensionality").
Dimensionsreducering är en samling tekniker för att på ett intelligent sätt "komprimera" datan. Målet är att koka ner de hundratals variablerna till ett fåtal nya, super-variabler som fortfarande behåller den viktigaste informationen. Det är som att skriva en sammanfattning av en bok – du behåller kärnan i handlingen men tar bort onödiga detaljer.
Varför är detta viktigt? Detta är inte bara en teoretisk städövning, utan en kritisk process för att göra maskininlärning praktiskt genomförbar.
- Snabbare träning: En modell som tränar på 10 "super-variabler" istället för 1000 originalvariabler kan bli klar på minuter istället för dagar, vilket sparar enorma mängder tid och beräkningskraft.
- Minskat brus: Många av de ursprungliga variablerna kan vara redundanta (t.ex. ha både längd i meter och i fot) eller bara vara "brus". Dimensionsreducering hjälper till att filtrera bort detta och fokusera på den verkliga underliggande signalen, vilket ofta leder till mer träffsäkra modeller.
- Visualisering: Genom att reducera dimensionerna till 2 eller 3 kan vi faktiskt plotta datan i en graf och visuellt inspektera den. Detta är ett oerhört kraftfullt verktyg för att förstå datans struktur och se om det finns tydliga kluster innan man ens börjar träna en mer komplex modell.

Fallstudie: K-Means Clustering i Praktiken
Detta är den mest kända clustering-algoritmen. Målet är att dela in datan i K antal fördefinierade kluster. Algoritmen fungerar iterativt:
- Den startar med K slumpmässiga mittpunkter (centroider).
- Den tilldelar varje datapunkt till den närmaste centroiden.
- Den räknar om positionen för varje centroid baserat på medelvärdet av alla punkter som nu tillhör den.
- Den upprepar steg 2 och 3 tills centroiderna slutar flytta på sig.
Låt oss se detta i praktiken. Koden nedan skapar först slumpmässig "blob"-data (som ser ut som moln av punkter) och använder sedan KMeans från scikit-learn för att hitta de underliggande grupperna.
# kmeans_visualization.py
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
# 1. Skapa ett syntetiskt dataset
# Vi skapar 300 punkter fördelade runt 4 olika centrum.
X, y_true = make_blobs(n_samples=300, centers=4,
cluster_std=0.70, random_state=0)
# 2. Skapa och träna K-Means modellen
# Vi talar om för modellen att vi letar efter 4 kluster.
kmeans = KMeans(n_clusters=4, random_state=0, n_init=10)
kmeans.fit(X)
y_kmeans = kmeans.predict(X)
# 3. Visualisera resultatet
plt.figure(figsize=(8, 6))
# Rita upp punkterna, färgade efter det kluster de tilldelats av KMeans
plt.scatter(X[:, 0], X[:, 1], c=y_kmeans, s=50, cmap='viridis')
# Rita ut de slutgiltiga centroiderna som modellen hittat
centers = kmeans.cluster_centers_
plt.scatter(centers[:, 0], centers[:, 1], c='red', s=200, alpha=0.75, marker='X')
plt.title("K-Means Clustering i Aktion")
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.show()
Resultatet är en tydlig visualisering av oövervakad inlärning. Vi gav aldrig modellen något facit (y_true används inte i träningen), men den lyckades ändå på egen hand identifiera de fyra distinkta grupperna i datan och hitta deras mittpunkter. Detta är kärnan i clustering: att hitta struktur i data utan förkunskap.
Svenska exempel
Under 2000-talet, när datorkraften och mängden tillgänglig data exploderade, blev maskininlärning ryggraden i många av de mest framgångsrika tech-bolagen.
-
Klarna: Hela Klarnas affärsmodell bygger på att snabbt kunna fatta ett kreditbeslut. När du klickar på "Köp nu, betala senare" måste de på en bråkdels sekund avgöra hur troligt det är att du faktiskt kommer att betala. Detta görs med en regressionsmodell som tar hundratals variabler (din köphistorik, tid på dygnet, typ av produkt, etc.) och producerar en "riskpoäng". Detta är klassisk maskininlärning i ett nötskal, där AI inte bara är en teknisk finess, utan en direkt förutsättning för hela affären.
-
Spotify: Hur kan Spotify upplevas som att den "känner" din musiksmak? Deras rekommendationsmotorer är ett sofistikerat exempel på hur flera ML-tekniker samverkar.
- Kollaborativ filtrering (Unsupervised): Systemet använder clustering för att hitta andra användare vars musiksmak liknar din. Den tittar sedan på vilka låtar de lyssnar på, som du ännu inte har upptäckt, och rekommenderar dem till dig. Den antar att om personer med liknande historisk smak gillar en viss låt, är chansen stor att du också kommer att göra det.
- Content-Based Filtering (Supervised): Spotify analyserar även ljudfilerna direkt för att extrahera hundratals musikaliska egenskaper (tempo, akustiskhet, "dansbarhet", tonart etc.). När du lyssnar mycket på en viss typ av låt, tränas en klassificeringsmodell att förstå din "smakprofil". Den kan sedan hitta helt nya låtar från okända artister som har liknande musikaliska egenskaper och rekommendera dem till dig.
Verifiera dessa studier med tech-bloggarna som beskriver användningen.
Perspektiv på Klassisk Maskininlärning
Det filosofiska perspektivet: Empirismens Återkomst
Maskininlärning representerar en seger för empirismen. AI-forskare slutade försöka förprogrammera "sanningen". Istället skapade de system som kunde lära sig från erfarenhet (data). En ML-modell är en modern "Tabula Rasa", en oskriven tavla som fylls med kunskap genom observation av världen. Detta skifte från logik till statistik är det enskilt viktigaste som hänt i AI:s historia.
Det ekonomiska perspektivet: Från Akademisk Teori till Affärskritisk Motor
Den kanske största anledningen till att maskininlärning exploderade var att den skapade ett enormt och mätbart ekonomiskt värde. För första gången kunde företag gå från att reagera på vad deras kunder hade gjort, till att med hög precision förutsäga vad de skulle komma att göra. Amazons rekommendationsmotor, som föreslår produkter baserat på vad liknande kunder har köpt, är ett klassiskt exempel. Denna funktion, driven av maskininlärning, sägs stå för över en tredjedel av deras totala försäljning. ML var inte längre en akademisk leksak; det var en affärskritisk motor som kunde omvandla data till miljarder.
Det sociologiska perspektivet: Filterbubblor och Polarisering
När företag som YouTube och Facebook (numera Meta) började använda maskininlärning för att personalisera sina flöden, uppstod en oavsiktlig men kraftfull bieffekt. Genom att ständigt visa oss innehåll som liknar det vi redan gillat, skapade algoritmerna det som aktivisten Eli Pariser kallade filterbubblor.
Algoritmerna har inget ont uppsåt. Deras enda mål är att maximera din tid på plattformen. Men genom att optimera för engagemang, råkar de oavsiktligt gynna innehåll som är chockerande, upprörande eller extremt. Detta är en oavsiktlig konsekvens av ett system som är designat för att fånga din uppmärksamhet, inte för att ge dig en balanserad bild av världen.
Ditt personliga informationsuniversum skulpteras av algoritmer för att passa just dina preferenser. Problemet är att du inte ser vad som filtreras bort. Detta förstärks av algoritmisk amplifiering: innehåll som väcker starka känslor (ilska, rädsla, glädje) genererar mer engagemang (klick, kommentarer, delningar) och blir därför automatiskt prioriterat av systemet. Resultatet är att mer extrema eller polariserande budskap ofta får större spridning. Över tid kan detta leda till att vi hamnar i eko-kammare, där våra egna åsikter ständigt upprepas och förstärks, och vi får en förvrängd bild av hur "den andra sidan" tänker. Detta har pekats ut som en starkt bidragande orsak till den ökande politiska polariseringen i många länder. Detta går även att utnyttja för att uppnå specifika ändamål. Det mest ökända exemplet är Cambridge Analytica-skandalen, där ML-driven mikrotargeting användes för att försöka påverka valutgången i USA 2016.
Mikrotargeting är användningen av onlinedata för att skräddarsy reklambudskap till individer, baserat på identifiering av mottagarnas personliga sårbarheter.
Det kognitiva & psykologiska perspektivet: Uppmärksamhetsekonomin och den Algoritmiska Blicken
Varför är dessa personaliserade flöden så beroendeframkallande? Svaret ligger i hur de är designade för att utnyttja vårt belöningssystem. I en värld av informationsöverflöd är den verkliga bristvaran mänsklig uppmärksamhet. Hela affärsmodellen för många sociala medier bygger på att fånga och behålla din uppmärksamhet så länge som möjligt, för att kunna visa dig så många annonser som möjligt.
För att göra detta använder de en psykologisk mekanism som kallas intermittent förstärkning. Det är samma princip som gör spelautomater så beroendeframkallande. Du vet aldrig när nästa belöning (en rolig video, en intressant nyhet, en like på din bild) kommer att dyka upp. Denna oförutsägbarhet gör att din hjärna fortsätter att utsöndra dopamin, vilket får dig att fortsätta skrolla.
Över tid kan detta leda till att vi utvecklar en "algoritmiskt synsätt", där vi omedvetet börjar anpassa vårt eget beteende för att maximera den belöning vi får från plattformen. Vi börjar posta bilder, skriva inlägg eller skapa videor som vi tror att algoritmen kommer att gilla, snarare än vad som är mest autentiskt för oss själva. Vi börjar se på vår egen verklighet genom linsen av "kommer detta att fungera online?". Tänk på detta om ni någonsin tycker att influencer livsstilen känns lockande.
Övningar
Machine Learning
I detta kapitel går vi från den teoretiska förståelsen av neuroner och nätverk till att experimentera med dem. Läroboken har redan visat dig hur man bygger dessa modeller från grunden och hur man använder professionella verktyg. I dessa övningar kommer du att ta den koden, modifiera den, och testa dess gränser för att bygga en djupare, praktisk intuition.
Verktyg: Visual Studio Code med en Python-installation och nödvändiga bibliotek (numpy, scikit-learn, matplotlib).
Förberedelser: Hämta Koden
Innan du börjar, kopiera kodexemplen för Perceptron, NeuralNetwork, DecisionTreeClassifier och KMeans från läroboken till dina egna Python-filer. Du kommer att utgå från dessa i övningarna.
Övning 1: Bevisa Perceptronens Gränser
Syfte: Att praktiskt demonstrera varför en enskild Perceptron inte kan lösa icke-linjära problem som XOR, precis som Minsky och Papert argumenterade.
- Skapa en fil:
test_perceptron_limits.py. - Använd
Perceptron-klassen: Importera eller klistra inPerceptron-klassen från läroboken (den som har enfit-metod). - Skapa XOR-data:
import numpy as np
# Data för en XOR-grind
X_train = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_train = np.array([0, 1, 1, 0]) - Träna och Testa: Använd samma träningsloop som i textbokens "fest-scenario"-exempel, men med din XOR-data. Träna i ett stort antal epoker (t.ex. 500).
# Skapa och träna din Perceptron
p = Perceptron(num_inputs=2, n_iterations=500)
p.fit(X_train, y_train)
# Testa den tränade modellen
print("\nTestar den tränade Perceptronen på XOR-data:")
for inputs, target in zip(X_train, y_train):
prediction = p.predict(inputs)
print(f"Input: {inputs}, Prediktion: {prediction}, Korrekt: {target}") - Analysera: Kör skriptet. Kommer din Perceptron någonsin att lära sig att lösa XOR-problemet korrekt för alla fyra fallen? Vad händer med vikterna och biasen under träningens gång?
Du har nu praktiskt bevisat Perceptronens begränsning. Den kan inte hitta en rak linje som separerar XOR-datan. Detta motiverar varför vi behöver gå vidare till nästa nivå: nätverk av neuroner.
Övning 2: Modifiera ett Neuralt Nätverk
Syfte: Att förstå hur arkitekturen i ett neuralt nätverk påverkar dess förmåga att lära sig, genom att modifiera nätverket som löste XOR i läroboken.
- Använd
NeuralNetwork-koden: Ta koden förNeuralNetworkochNeuronfrån textboken som framgångsrikt löste XOR. - Experiment 1: Färre Neuroner. Ändra arkitekturen från
[2, 2, 1](två input, två dolda, en output) till[2, 1, 1]. Du har nu skapat en "flaskhals" med bara en enda neuron i det dolda lagret. Kör träningskoden igen. Kan nätverket fortfarande lösa XOR? Varför/varför inte? - Experiment 2: Djupare Nätverk. Ändra arkitekturen till
[2, 4, 4, 1]. Du har nu skapat ett djupare nätverk med två dolda lager. Träna modellen igen. Lär den sig snabbare eller långsammare? Blir resultatet mer stabilt?
Du har nu agerat som en AI-arkitekt. Hur påverkade antalet neuroner och lager modellens prestanda? Detta är kärnan i hyperparameter-tuning: att hitta den bästa arkitekturen och de bästa inställningarna för ett givet problem.
Övning 3: Tolka ett Beslutsträd
Syfte: Att översätta det visuella beslutsträdet från läroboken till en uppsättning läsbara IF/ELSE-regler, för att bevisa dess status som en "white box"-modell.
- Kör koden: Kör Python-koden från "Fallstudie 2: Beslutsträd i Praktiken" i textboken för att generera bilden av det tränade Iris-trädet.
- Analysera bilden: Titta på den översta noden (roten).
- Vilken fråga ställer den? (t.ex.
petal length (cm) <= 2.45). - Om
True, vart leder den? OmFalse, vart leder den?
- Vilken fråga ställer den? (t.ex.
- Skriv ner reglerna: Följ varje möjlig väg från roten ner till ett löv och skriv ner det som en
IF/ELSE-sats i pseudokod eller vanlig text.- Exempel på en regel:
IF petal length <= 2.45 THEN
CLASSIFY AS: setosa
ELSE
IF petal width <= 1.75 THEN
... (fortsätt följa trädet)
- Exempel på en regel:
- Jämför: Har du nu en komplett uppsättning regler som exakt beskriver hur modellen fattar sina beslut?
Jämför detta med det neurala nätverket. Skulle du kunna göra samma sak där – titta på de inlärda vikterna och bias-värdena och skriva ner en uppsättning enkla, mänskligt läsbara regler? Varför är denna egenskap (tolkningsbarhet) så viktig i vissa tillämpningar, som medicinsk diagnostik eller kreditbedömning?
Övning 4: Konsten att Välja rätt 'K'
Syfte: Att utforska en av de största utmaningarna med K-Means clustering: att i förväg veta hur många kluster (K) man ska leta efter.
- Använd K-Means-koden: Ta koden från "Fallstudie: K-Means Clustering i Praktiken" i textboken. Den använder
make_blobsför att skapa data med 4 tydliga kluster. - Experimentera med
K:- Kör koden precis som den är, med
n_clusters=4. Resultatet bör se perfekt ut. - Ändra nu
n_clusterstill2. Kör igen. Hur försöker algoritmen tvinga in datan i bara två grupper? - Ändra
n_clusterstill8. Kör igen. Vad händer nu? Ser du hur några av de naturliga klustren har blivit godtyckligt uppdelade?
- Kör koden precis som den är, med
- Visualisera: Spara bilderna från varje körning och jämför dem sida vid sida.
Denna övning visar att även om K-Means är kraftfullt, kräver det ofta mänsklig expertis för att välja rätt hyperparametrar. I verkliga dataset (som kunddata) vet man sällan det "rätta" antalet kluster i förväg. Hur skulle du kunna gå tillväga för att hitta ett rimligt K för ett helt okänt dataset? (Detta är ett aktivt forskningsområde, med metoder som "Elbow Method" och "Silhouette Score").