| 36 | |
| 37 | |
| 38 | class SVM: |
| 39 | def __init__(self, kernel, C=1.0): |
| 40 | self.kernel = kernel |
| 41 | self.C = C |
| 42 | |
| 43 | def _loss(self, X, Y): |
| 44 | # return -np.sum(self.alphas) + \ |
| 45 | # 0.5 * np.sum(np.outer(Y, Y) * self.kernel(X, X) * np.outer(self.alphas, self.alphas)) |
| 46 | return -np.sum(self.alphas) + \ |
| 47 | 0.5 * np.sum(self.YYK * np.outer(self.alphas, self.alphas)) |
| 48 | |
| 49 | def _take_step(self, i1, i2): |
| 50 | # returns True if model params changed, False otherwise |
| 51 | |
| 52 | # Skip if chosen alphas are the same |
| 53 | if i1 == i2: |
| 54 | return False |
| 55 | |
| 56 | alph1 = self.alphas[i1] |
| 57 | alph2 = self.alphas[i2] |
| 58 | y1 = self.Ytrain[i1] |
| 59 | y2 = self.Ytrain[i2] |
| 60 | E1 = self.errors[i1] |
| 61 | E2 = self.errors[i2] |
| 62 | s = y1 * y2 |
| 63 | |
| 64 | # Compute L & H, the bounds on new possible alpha values |
| 65 | if (y1 != y2): |
| 66 | L = max(0, alph2 - alph1) |
| 67 | H = min(self.C, self.C + alph2 - alph1) |
| 68 | elif (y1 == y2): |
| 69 | L = max(0, alph1 + alph2 - self.C) |
| 70 | H = min(self.C, alph1 + alph2) |
| 71 | if (L == H): |
| 72 | return False |
| 73 | |
| 74 | # Compute kernel & 2nd derivative eta |
| 75 | k11 = self.kernel(self.Xtrain[i1], self.Xtrain[i1]) |
| 76 | k12 = self.kernel(self.Xtrain[i1], self.Xtrain[i2]) |
| 77 | k22 = self.kernel(self.Xtrain[i2], self.Xtrain[i2]) |
| 78 | eta = k11 + k22 - 2 * k12 |
| 79 | |
| 80 | # Usual case - eta is non-negative |
| 81 | if eta > 0: |
| 82 | a2 = alph2 + y2 * (E1 - E2) / eta |
| 83 | # Clip a2 based on bounds L & H |
| 84 | if (a2 < L): |
| 85 | a2 = L |
| 86 | elif (a2 > H): |
| 87 | a2 = H |
| 88 | # else a2 remains unchanged |
| 89 | |
| 90 | # Unusual case - eta is negative |
| 91 | # alpha2 should be set to whichever extreme (L or H) that yields the lowest |
| 92 | # value of the objective |
| 93 | else: |
| 94 | print("***** eta < 0 *****") |
| 95 | # keep it to assign it back later |