Neuronale Netze

Motivation

Um mit den bisherigen Verfahren Lineare Regression und Logistische Regression nichtlineare Vorhersagen zu treffen, muss man die Zahl der Merkmale um polynomiale Merkmale erhöhen. Auf den Seiten NRW Wahl 2017 Gesamtergebnis und NRW Wahl 2017 Düsseldorf können Sie mit höhergradigen Merkmalen für das Problem den Wahlerfolg einer Partei aus den Ergebnissen zweier anderer vorherzusagen spielen. Hat man zwei Merkmale \(x_1,x_2\) (also zum Beispiel zwei Parteiergebnisse) und wählt alle als Hypothesenfunktion die Funktion \[\Sigma_{i=0}^6\Sigma_{j=0}^i \theta_{i,j}x_1^ix_2^{i-j}\] sprich die Funktion mit allen polynomialen Merkmalen bis zum Grad 6, so erhält man 28 Merkmale.

mapFeature(c(2),c(3),6)
##      x1^0*x2^0 x1^1*x2^0 x1^0*x2^1 x1^2*x2^0 x1^1*x2^1 x1^0*x2^2 x1^3*x2^0
## [1,]         1         2         3         4         6         9         8
##      x1^2*x2^1 x1^1*x2^2 x1^0*x2^3 x1^4*x2^0 x1^3*x2^1 x1^2*x2^2 x1^1*x2^3
## [1,]        12        18        27        16        24        36        54
##      x1^0*x2^4 x1^5*x2^0 x1^4*x2^1 x1^3*x2^2 x1^2*x2^3 x1^1*x2^4 x1^0*x2^5
## [1,]        81        32        48        72       108       162       243
##      x1^6*x2^0 x1^5*x2^1 x1^4*x2^2 x1^3*x2^3 x1^2*x2^4 x1^1*x2^5 x1^0*x2^6
## [1,]        64        96       144       216       324       486       729
dim(mapFeature(c(2),c(3),6))
## [1]  1 28

Neuronale Netze ermöglichen es, nichtlineare Hypothesen zu formulieren, ohne die Zahl der Merkmale zu erhöhen.

Modell

  • Die Zellen eines neuronalen Netzes sind in Schichten angeordnet.
  • Die erste Schicht heißt Inputschicht, die letzte Outputschicht und die dazwischen heißen hidden layers.
  • Jede Zelle hat eine Input-Kante von jeder Zelle der vorangegangenen Schicht plus eine Kante von der extra Bias-Zelle (entspricht unserer alten Annahme \(x_0=1\) gewichtet mit dem Parameter \(\theta_0\)).
  • Jede Zelle wird mit einem Aktivierungspotential abhängig von den gewichteten Werten der Eingangskanten aktiviert und berechnet aus dem Aktivierungspotential den Outputwert der Zelle.
  • Die Aktivierungsfunktion ist die log(sigmoid)-Funktion (wie gehabt).
  • Jede Zelle hat Outputkanten zu allen Zellen der nächshöheren Schicht an die der Outputwert der Zelle weitergereicht wird.

Wichtige Notationen:

  • die Schichten werden mit Superskript und die Position der Zelle innerhalb einer Schicht mit Subskript angegeben (Subskript 0 bezeichnet die Biaszellen und Superskript 1 die Inputschicht).
  • \(a_i^{(j)}\) bezeichnet das Aktivierungswert der i-ten Zelle in der j-ten Schicht (\(a_0^{0}(j)}=0\) für alle \(i\)).
  • \(\theta^{(j)}\) bezeichnet die Matrix der Parameter, die die Kanten von der j-ten zur (j+1)-ten Schicht gewichten. Die Matrix \(\theta^{j}\) hat soviele Zeilen, wie die Schicht \(j+1\) Zellen und eine Spalte mehr als die Schicht \(j\) Zellen hat (die Extraspalte wird für die Bias-Zelle benötigt, die wir bei der Zahl der Zellen nicht mitzählen).
  • \(\theta_{i,k}^{(j)}\) bezeichnet das Gewicht der Kante von der Zelle \(x_k^{(j)}\) zur Zelle \(x_i^{(j+1)}\)
  • \(a_i^{(j)}=g(\theta^{(j-1)}\times a^{(j-1)})\)

Klassifikation mit mehreren Klassen

Anstatt die Strategie 1-versus-all anzuwenden, wenn wir in mehr als zwei Klassen klassifizieren wollen, wählen wir die Outputschicht so, dass sie genausoviele Zellen hat, wie unser Problem Klassen. Die n-te Klasse wird nun über einen Outputvektor repräsentiert, der überall 0-en und nur an der n-ten Position eine 1 hat.

OCR für Ziffern: Logistic Regression

Gegeben eine Menge von handgeschriebenen Ziffern klassifiziert nach den dargestellten Ziffern, gesucht ist ein Algorithmus, der zuverlässig neue Ziffern erkennen kann.

In der Variable X haben wir jetzt eine Matrix der Dimension 5000, 400. Sprich 5000 Bilder jeweils kodiert in 20*20 Pixeln.

# Randomly select 100 data points to display
rand_indices <- sample(m)
sel <- X[rand_indices[1:100], ]

displayData(sel)

Hier die Durchschnitte der Input-Bilder:

average <- c()
for (i in 1:10){
  down <- (i-1)*500+1
  up <- i*500
  numberi <- X[down:up,]
  average <- rbind(average,apply(numberi,2,mean))
}
displayData(average)

Wir werden zunächst einen Klassifikator mit logistischer Regression trainieren.

lambda <- 1
classes <- 10
#XsubIndex <- sample(5000,5)
#X <- X[XsubIndex,]
#y <- y[XsubIndex,]
oneVersusAll_theta <- oneVersusAll(X, y, classes, lambda)

Diesen Klassifikator können wir jetzt für einzelne Vorhersagen nutzen:

for (i in 1:10){
  ex <- sample(5000,1)
  displayData(X[ex,])
  Xex <- matrix(X[ex,],nrow=length(ex))
  predict <- predictOneVersusAll(Xex,oneVersusAll_theta)
  real <- y[ex] 
  real <- real %% 10 # modulo 10 in order to set class 10 back to 0.
  predict <- predict %% 10 # modulo 10 in order to set class 10 back to 0.
  print(paste("predict number:",predict,"real number:", real))
  }
## [1] "predict number: 3 real number: 3"
## [1] "predict number: 9 real number: 9"
## [1] "predict number: 5 real number: 5"
## [1] "predict number: 6 real number: 6"
## [1] "predict number: 0 real number: 0"
## [1] "predict number: 0 real number: 0"
## [1] "predict number: 8 real number: 8"
## [1] "predict number: 2 real number: 2"
## [1] "predict number: 4 real number: 4"
## [1] "predict number: 4 real number: 4"

Wir können aber auch alle Ziffern vorhersagen lassen und die Accuracy berechnen:

predict_all <- predictOneVersusAll(X,oneVersusAll_theta)
acc <- mean(predict_all == y) *100

Die Accuracy ist 92.96.

Beachte, dass wir auf denselben Daten trainiert und getestet haben. Dies birgt immer das Risiko des Overfittings, wir werden daher nun noch einen Durchlauf machen, in dem wir auf 90% der Daten trainieren und auf den übrigen 10% testen.

# Choosing training and test data:
m <- dim(X)[1]
n <- dim(X)[1]
t <- round(m/10)
testIndex <- sample(m)[1:t]
Xtest <- X[testIndex, ]
Xtrain <- X[-testIndex,]
ytest <- y[testIndex]
ytrain <- y[-testIndex]

# training the one-versus-all classificator
lambda <- 1
classes <- 10
oneVersusAll_theta <- oneVersusAll(Xtrain, ytrain, classes, lambda)

# predicting the values for the test data and calculate accuracy
predict_all <- predictOneVersusAll(Xtest,oneVersusAll_theta)
acc <- mean(predict_all == ytest) *100

Die Testdaten werden mit einer Genauigkeit von 89.8% vorhergesagt.

Das Trainieren des one-versus-all Klassifikators benötigt relativ viel Zeit, da pro Klasse ein eigenes logistisches Regressionsmodell trainiert werden muss.

OCR Klassifikation mit Neuronalen Netzen

Zunächst legen wir die Architektur unseres neuronalen Netzes fest:

# Load data
load('ex3data1.rda')
list2env(data,.GlobalEnv)
## <environment: R_GlobalEnv>
rm(data)


## Setup the parameters you will use for this exercise
input_layer_size  <- 400;  # 20x20 Input Images of Digits
hidden_layer_size <- 25;   # 25 hidden units
classes <- 10;          # 10 labels, from 1 to 10   
                          # (note that we have mapped "0" to label 10)

# Load the pretrained weights / parameters Theta1 and Theta2
load('ex3weights.Rda')
list2env(data,.GlobalEnv)
## <environment: R_GlobalEnv>
rm(data)

Wir haben jetzt ein neuronales Netz mit einem Input layer der Größe 400, einem Hidden Layer der Größe 25 und einem Output Layer der Größe 10 definiert. Die Dimension von \(\theta^{(0)}\) ist dementsprechend 25, 401 und die von \(\theta^{(1)}\) ist 10, 26.

Mit diesem neuronalen Netz können wir jetzt Vorhersagen mittels feedforward propagation machen:

pred <- predictNN(X,Theta1,Theta2)$prediction

acc <- mean(pred==y) * 100

Die Vorhersagen haben eine Genauigkeit von 97.52%.

Hier noch ein paar Beispielvorhersagen:

  ex <- sample(5000,10)
for (i in ex){
  displayData(X[i,])
  Xex <- matrix(X[i,],nrow=length(i))
  predict <- predictNN(Xex,Theta1,Theta2)$prediction
  real <- y[i] 
  real <- real %% 10 # modulo 10 in order to set class 10 back to 0.
  predict <- predict %% 10 # modulo 10 in order to set class 10 back to 0.
  print(paste("predict number:",predict,"real number:", real))
  }
## [1] "predict number: 8 real number: 8"
## [1] "predict number: 9 real number: 9"
## [1] "predict number: 2 real number: 2"
## [1] "predict number: 3 real number: 3"
## [1] "predict number: 4 real number: 4"
## [1] "predict number: 3 real number: 3"
## [1] "predict number: 0 real number: 0"
## [1] "predict number: 7 real number: 7"
## [1] "predict number: 8 real number: 8"
## [1] "predict number: 2 real number: 2"

Im obigen Beispiel haben wir mit einem bereits vortrainierten Netz gearbeitet. Nun werden wir die Parameter des neuronalen Netzes selbst trainieren. Hierzu nutzen wir den backpropagation Algorithmus.

Wir benutzen die Kostenfunktion für neuronale Netze:

# unroll parameters


nn_params <-c(c(Theta1),c(Theta2))

lambda <- 0
cost_noReg <- CostFunctionNN(input_layer_size, hidden_layer_size, classes,X, y, lambda)(nn_params) 

lambda <- 1
cost_Reg <- CostFunctionNN(input_layer_size, hidden_layer_size, classes,X, y, lambda)(nn_params) 

Die Kosten für die vortrainierten Parameter belaufen sich ohne Regularisierung auf 0.2876292 und mit Regularisierung ($=$1) auf 0.3837699.

Als nächstes rufen wir die Funktion zur Gradientenbestimmung auf. Hierzu müssen wir zunächst die Parameter initialisieren. Vorsicht: Die Parameter sollten zufällig initialisiert werden, denn werden alle Parameter eines Layers auf denselben Wert gesetzt, so erhalten alle Zellen des Nachfolgelayers dasselbe Aktivierungspotential.

hidden_layer_size <- 25;   # 25 hidden units

# initialize parameters
initial_Theta1 <- randInitializeWeights(input_layer_size, hidden_layer_size)
initial_Theta2 <- randInitializeWeights(hidden_layer_size, classes)

# Unroll parameters
initial_nn_params <- c(initial_Theta1,initial_Theta2)

# Try different values of lambda
lambda <- 10 # try 1, 100, 1000, 10000

# Create "short hand" for the cost function to be minimized
costFunction <- CostFunctionNN(input_layer_size, hidden_layer_size, 
                                   classes, X, y, lambda) #over nn_params

gradFunction <- GradFunctionNN(input_layer_size, hidden_layer_size, 
                               classes, X, y, lambda) #over nn_params

# Now, costFunction and gradFunction are functions that take in only one argument (the
# neural network parameters)


# maxit determines how many training is performed (play with the value)
opt <- lbfgsb3_(initial_nn_params, fn= costFunction, gr=gradFunction,
                  control = list(trace=1,maxit=50))
## This problem is unconstrained.
## At iteration  0  f = 6.918046
## At iteration  2  f = 4.382569
## At iteration  3  f = 3.304774
## At iteration  4  f = 3.300055
## At iteration  5  f = 3.283462
## At iteration  6  f = 3.168527
## At iteration  7  f = 3.019232
## At iteration  8  f = 2.802168
## At iteration  9  f = 2.514399
## At iteration  10  f = 2.282309
## At iteration  11  f = 2.038313
## At iteration  12  f = 1.866893
## At iteration  13  f = 1.764616
## At iteration  14  f = 1.667788
## At iteration  15  f = 1.553399
## At iteration  16  f = 1.494607
## At iteration  17  f = 1.451037
## At iteration  18  f = 1.399745
## At iteration  19  f = 1.365587
## At iteration  20  f = 1.336838
## At iteration  21  f = 1.303861
## At iteration  22  f = 1.261485
## At iteration  23  f = 1.231109
## At iteration  24  f = 1.211539
## At iteration  25  f = 1.194892
## At iteration  26  f = 1.182291
## At iteration  27  f = 1.167282
## At iteration  28  f = 1.149426
## At iteration  29  f = 1.131873
## At iteration  30  f = 1.122178
## At iteration  31  f = 1.111812
## At iteration  32  f = 1.10676
## At iteration  33  f = 1.097758
## At iteration  34  f = 1.093236
## At iteration  35  f = 1.088706
## At iteration  36  f = 1.082141
## At iteration  37  f = 1.079005
## At iteration  38  f = 1.074037
## At iteration  39  f = 1.071057
## At iteration  40  f = 1.067004
## At iteration  41  f = 1.061896
## At iteration  42  f = 1.058808
## At iteration  43  f = 1.055794
## At iteration  44  f = 1.053721
## At iteration  45  f = 1.051593
## At iteration  46  f = 1.050312
## At iteration  47  f = 1.047184
## At iteration  48  f = 1.045752
## At iteration  49  f = 1.044215
## At iteration  50  f = 1.041669
## At iteration  51  f = 1.041307
nn_params <- opt$prm
cost <- opt$f

# Obtain Theta1 and Theta2 back from nn_params
Theta1 <- matrix(nn_params[1:(hidden_layer_size * (input_layer_size + 1))],
                 nrow=hidden_layer_size, ncol=(input_layer_size + 1))

Theta2 <- matrix(nn_params[(1 + (hidden_layer_size * (input_layer_size + 1))):length(nn_params)],
                 nrow=classes, ncol=(hidden_layer_size + 1))

# Visualize Weights:
#  displaying the hidden units

displayData(Theta1[, -1])

#  displaying the output units

displayData(Theta2[, -1])

#  displaying the product of both theta matrices
displayData(t(t(Theta1[, -1]) %*% t(Theta2[,-1])))

Als nächstes berechnen wir die Accuracy unseres trainierten Netzes:

pred <- predictNN(X,Theta1,Theta2)$prediction
acc <-  mean(pred==y) * 100

Die Accuracy ist 93.58.