From 6b86055a4f553c478aa480314143f856fbb03833 Mon Sep 17 00:00:00 2001 From: saeedkhosravi94 Date: Sat, 8 Nov 2025 18:40:55 +0100 Subject: [PATCH] adding NewtonUTSVM --- NewtonUTSVM.py | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100755 NewtonUTSVM.py diff --git a/NewtonUTSVM.py b/NewtonUTSVM.py new file mode 100755 index 0000000..a93ecdf --- /dev/null +++ b/NewtonUTSVM.py @@ -0,0 +1,160 @@ +import numpy as np + +class NewtonUTSVM: + def __init__(self, X, y, U, C, eps): + self.X = np.asarray(X, dtype=np.float64) + self.y = np.asarray(y, dtype=np.float64).reshape(-1, 1) + self.U = np.asarray(U, dtype=np.float64) + self.C = np.asarray(C, dtype=np.float64) + self.eps = eps + + def fit(self): + np.random.seed(42) + self.w1 = np.random.normal(0, 0.01, (self.X.shape[1], 1)) + self.b1 = 0.0 + self.w2 = np.random.normal(0, 0.01, (self.X.shape[1], 1)) + self.b2 = 0.0 + + for _ in range(5): + self.w1, self.b1 = self.plane1(self.X, self.y, self.U, + self.C[0], self.C[1], self.C[2], self.eps) + self.w2, self.b2 = self.plane2(self.X, self.y, self.U, + self.C[3], self.C[4], self.C[5], self.eps) + + def predict(self, x_test): + x_test = np.asarray(x_test, dtype=np.float64) + + dist1 = self._safe_distance(x_test, self.w1, self.b1) + dist2 = self._safe_distance(x_test, self.w2, self.b2) + + y_pred = np.where(dist1 < dist2, 1, -1).reshape(-1, 1) + self.preds = y_pred + return y_pred + + def _safe_distance(self, X, w, b): + norm = np.linalg.norm(w) + if norm < 1e-10: + return np.full((X.shape[0],), np.inf) + return np.abs(X @ w + b) / norm + + def plane1(self, X, y, U, C1, C2, C3, eps): + A = X[y[:,0] == 1] + B = X[y[:,0] == -1] + + T1 = np.hstack([A, np.ones((A.shape[0], 1))]) + T2 = np.hstack([B, np.ones((B.shape[0], 1))]) + T3 = np.hstack([U, np.ones((U.shape[0], 1))]) + + Z = np.random.normal(0, 0.01, (X.shape[1]+1, 1)) + prev_Z = np.zeros_like(Z) + + learning_rate = 0.1 + best_loss = float('inf') + + for count in range(100): + e2 = np.ones((B.shape[0], 1)) + eu = np.ones((U.shape[0], 1)) + + margin_B = e2 + T2 @ Z + margin_U = (-1 + eps)*eu - T3 @ Z + + grad = (T1.T @ (T1 @ Z) + + C1 * T2.T @ self.func(margin_B, 'pf') + + C2 * Z - + C3 * T3.T @ self.func(margin_U, 'pf')) + + D1 = self.mat_diag(self.func(margin_B, 'pf') > 0) + D2 = self.func(margin_U, 'pf') > 0 + hessian = (T1.T @ T1 + + C1 * T2.T @ D1 @ T2 + + C2 * np.eye(Z.shape[0]) + + C3 * T3.T @ np.diag(D2.flatten()) @ T3) + + hessian += 1e-4 * np.eye(hessian.shape[0]) + + delta = np.linalg.solve(hessian, grad) + Z -= learning_rate * delta + + current_loss = np.linalg.norm(grad) + if current_loss < best_loss: + best_loss = current_loss + learning_rate = min(learning_rate * 1.1, 1.0) + else: + learning_rate = max(learning_rate * 0.5, 1e-4) + + if np.linalg.norm(Z - prev_Z) < self.eps: + break + prev_Z = Z.copy() + + return Z[:-1], Z[-1][0] + + def plane2(self, X, y, U, C4, C5, C6, eps): + A = X[y[:,0] == 1] + B = X[y[:,0] == -1] + + # Add bias terms + G1 = np.hstack([B, np.ones((B.shape[0], 1))]) + G2 = np.hstack([A, np.ones((A.shape[0], 1))]) + G3 = np.hstack([U, np.ones((U.shape[0], 1))]) + + Y = np.random.normal(0, 0.01, (X.shape[1]+1, 1)) + prev_Y = np.zeros_like(Y) + + learning_rate = 0.1 + best_loss = float('inf') + + for count in range(100): + e1 = np.ones((A.shape[0], 1)) + eu = np.ones((U.shape[0], 1)) + + margin_A = e1 - G2 @ Y + margin_U = (-1 + eps)*eu + G3 @ Y + + grad = (G1.T @ (G1 @ Y) - + C4 * G2.T @ self.func(margin_A, 'pf') + + C5 * Y + + C6 * G3.T @ self.func(margin_U, 'pf')) + + D3 = self.func(margin_A, 'pf') > 0 + D4 = self.func(margin_U, 'pf') > 0 + hessian = (G1.T @ G1 + + C4 * G2.T @ np.diag(D3.flatten()) @ G2 + + C5 * np.eye(Y.shape[0]) + + C6 * G3.T @ np.diag(D4.flatten()) @ G3) + + hessian += 1e-4 * np.eye(hessian.shape[0]) + + delta = np.linalg.solve(hessian, grad) + Y -= learning_rate * delta + + current_loss = np.linalg.norm(grad) + if current_loss < best_loss: + best_loss = current_loss + learning_rate = min(learning_rate * 1.1, 1.0) + else: + learning_rate = max(learning_rate * 0.5, 1e-4) + + if np.linalg.norm(Y - prev_Y) < self.eps: + break + prev_Y = Y.copy() + + return Y[:-1], Y[-1][0] + + def func(self, x, type='pf', ro=1e20): + if type == 'pf': + return np.maximum(0, x) + elif type == 'sm': + return x + (1/ro)*np.log(1+np.exp(-ro*x)) + + def mat_diag(self, m): + return np.diag(m.flatten()) + + def get_params(self): + return self.w1, self.b1, self.w2, self.b2 + + def get_preds(self): + return self.preds + + def score(self, y_test): + accuracy = np.sum(self.preds == y_test)/y_test.shape[0] + return accuracy \ No newline at end of file