Repository: dpuenteramirez/Semisupervised-learning-and-instance-selection-methods (language: python)

Issue:
Co-Training support dataset with two views
The algorithm is not implemented in the right way. It was a known issue, so this is correction of #36 
Co-Training to class

Co-Training to class



Discussion / hints:

/cib
Branch [issue-60-Co-Training_to_class](https://github.com/dpr1005/Semisupervised-learning-and-instance-selection-methods/tree/issue-60-Co-Training_to_class) created!
/cib
Branch [issue-60-Co-Training_to_class](https://github.com/dpr1005/Semisupervised-learning-and-instance-selection-methods/tree/issue-60-Co-Training_to_class) created!

Fix patch:
diff --git a/SemiSupervised Learning Algorithms/Co-Training.py b/SemiSupervised Learning Algorithms/Co-Training.py
deleted file mode 100644
index 46f9a0d..0000000
--- a/SemiSupervised Learning Algorithms/Co-Training.py
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env python
-# -*- coding:utf-8 -*-
-# @Filename:    Co-Training.py
-# @Author:      Daniel Puente Ramírez
-# @Time:        22/12/21 09:27
-
-import argparse
-import sys
-from math import floor
-from os import walk
-
-import matplotlib.pyplot as plt
-import numpy as np
-import pandas as pd
-from numpy.random import randint
-from sklearn.ensemble import RandomForestClassifier
-from sklearn.metrics import ConfusionMatrixDisplay, confusion_matrix
-from sklearn.metrics import classification_report
-from sklearn.model_selection import train_test_split
-from sklearn.utils import Bunch
-from tqdm import trange
-
-sys.path.insert(0, "../utils")
-from arff2dataset import arff2sk_dataset
-
-
-def co_training(p, n, k, u, L, U):
-    tot = p + n
-    counts = {}
-    for tar in L['class_names']:
-        counts[tar] = 0
-    for tar in L['target']:
-        counts[tar] += 1
-
-    p_class = max(counts, key=counts.get)
-
-    rng = np.random.default_rng()
-    model = RandomForestClassifier(n_jobs=-1)
-
-    __U__ = rng.choice(U['data'], size=u, replace=False, shuffle=False)
-
-    for _ in trange(k):
-
-        size = floor(len(L['data']) / 2)
-        rem = len(L['data']) - size
-        x1, y1 = L['data'][:size], L['target'][:size]
-        x2, y2 = L['data'][rem:], L['target'][rem:]
-        h1 = model.fit(x1, y1)
-        h2 = model.fit(x2, y2)
-
-        pred_1, pred_proba_1 = h1.predict(__U__), h1.predict_proba(__U__)
-        pred_2, pred_proba_2 = h2.predict(__U__), h2.predict_proba(__U__)
-
-        idx_1 = np.amax(pred_proba_1, axis=1)
-        aa = pd.DataFrame(pred_proba_1)
-        idx_2 = np.amax(pred_proba_2, axis=1)
-        prediction_1 = [[x, y, z, u_] for x, y, z, u_ in zip(pred_1,
-                                                             pred_proba_1,
-                                                             idx_1, __U__)]
-        prediction_2 = [[x, y, z, u_] for x, y, z, u_ in zip(pred_2,
-                                                             pred_proba_2,
-                                                             idx_2, __U__)]
-
-        prediction_1.sort(key=lambda x: x[2], reverse=True)
-        prediction_2.sort(key=lambda x: x[2], reverse=True)
-
-        predictions = np.concatenate((prediction_1, prediction_2), axis=0)
-
-        predictions_temp = pd.DataFrame(predictions, columns=['Class', 'Array',
-                                                              'Confidence',
-                                                              'Sample'])
-        predictions = []
-
-        p_added = 0
-        for _, pred in predictions_temp.iterrows():
-            if len(predictions) == tot:
-                break
-            if pred['Class'] == p_class and p_added < p:
-                predictions.append(pred)
-                p_added += 1
-            elif pred['Class'] != p_class:
-                predictions.append(pred)
-
-        predictions = pd.DataFrame(predictions, columns=['Class', 'Array',
-                                                         'Confidence',
-                                                         'Sample'])
-
-        samples = np.array([x for x in predictions.pop('Sample').to_numpy()],
-                           dtype=object)
-        classes = np.array([x for x in predictions.pop('Class').to_numpy()])
-
-        found = []
-        for index, s1 in enumerate(U['data']):
-            for s2 in samples:
-                if np.array_equal(s1, s2):
-                    found.append(index)
-                    break
-        U['data'] = np.delete(U['data'], found, axis=0)
-
-        found = []
-        for index, s1 in enumerate(__U__):
-            for s2 in samples:
-                if np.array_equal(s1, s2):
-                    found.append(index)
-                    break
-
-        __U__ = np.delete(__U__, found, axis=0)
-
-        L['data'] = np.concatenate((L['data'], samples), axis=0)
-        L['target'] = np.concatenate((L['target'], classes), axis=0)
-        try:
-            temp = rng.choice(U['data'], size=int(2 * p + 2 * n), shuffle=False)
-        except ValueError:
-            # If no more values remain for taking, it was not correctly
-            # parametrized
-            return L['data'], L['target']
-
-        __U__ = np.vstack([__U__, temp])
-
-    return L['data'], L['target']
-
-
-if __name__ == '__main__':
-    folder = '../datasets/'
-
-    files = next(walk(folder), (None, None, []))[2]
-    files_tot = '\t'.join(files)
-    parser = argparse.ArgumentParser()
-    parser.add_argument('--d', nargs=1)
-    args = parser.parse_args()
-
-    # if args.d[0] not in files_tot:
-    #     print('File does not exist.')
-    #     exit(1)
-    # dataset = [file for file in files if args.d[0] in file]
-    # if len(dataset) != 1:
-    #     print('More than one file found.')
-    #     exit(2)
-    dataset = ['iris.arff']
-
-    dataset = arff2sk_dataset(folder + dataset[0])
-
-    target_labels = [int(float(x)) for x in dataset.class_names]
-    y = dataset.target
-    X = dataset.data
-    X_test, X_train, y_test, y_train = train_test_split(X, y,
-                                                        train_size=0.95,
-                                                        stratify=y)
-    X = Bunch(data=X_train, target=y_train, class_names=target_labels)
-    y = Bunch(data=X_test)
-
-    data, target = co_training(p=3, n=9, k=30, u=20, L=X, U=y)
-
-    model = RandomForestClassifier(n_jobs=-1)
-    model.fit(data, target)
-    y_pred = model.predict(X_test)
-
-    print(classification_report(y_test, y_pred, labels=target_labels))
-
-    cm = confusion_matrix(y_test, y_pred, labels=target_labels)
-    dsp = ConfusionMatrixDisplay(confusion_matrix=cm,
-                                 display_labels=target_labels)
-    dsp.plot()
-    plt.show()
diff --git a/SemiSupervised Learning Algorithms/CoTraining.py b/SemiSupervised Learning Algorithms/CoTraining.py
new file mode 100644
index 0000000..e651255
--- /dev/null
+++ b/SemiSupervised Learning Algorithms/CoTraining.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+# @Filename:    CoTraining.py
+# @Author:      Daniel Puente Ramírez
+# @Time:        22/12/21 09:27
+# @Version:     2.0
+
+from math import floor
+
+import numpy as np
+from sklearn.naive_bayes import GaussianNB
+from sklearn.preprocessing import LabelEncoder
+
+
+class CoTraining:
+
+    def __init__(self, p=1, n=3, k=30, u=75, random_state=42):
+        self.p = p
+        self.n = n
+        self.k = k
+        self.u = u
+        self.random_state = random_state
+        self.size_x1 = 0
+        self.h1 = GaussianNB()
+        self.h2 = GaussianNB()
+
+    def fit(self, L, U, y):
+        if len(L) != len(y):
+            raise ValueError(
+                f'The dimension of the labeled data must be the same as the '
+                f'number of labels given. {len(L)} != {len(y)}'
+            )
+
+        le = LabelEncoder()
+        le.fit(y)
+        y = le.transform(y)
+        tot = self.n + self.p
+
+        self.size_x1 = floor(len(L[0]) / 2)
+
+        rng = np.random.default_rng()
+        u_random_index = rng.choice(len(U), size=floor(self.u),
+                                    replace=False, shuffle=False)
+
+        u_prime = U[u_random_index]
+        u1, u2 = np.hsplit(u_prime, self.size_x1)
+
+        for _ in range(self.k):
+            x1, x2 = np.hsplit(L, self.size_x1)
+            self.h1.fit(x1, y)
+            self.h2.fit(x2, y)
+
+            pred1, pred_proba1 = self.h1.predict(u1), self.h1.predict_proba(u1)
+            pred2, pred_proba2 = self.h2.predict(u2), self.h2.predict_proba(u2)
+
+            top_h1 = []
+            for index_p, p in enumerate(zip(pred1, pred_proba1)):
+                top_h1.append([p[0], np.amax(p[1]), index_p])
+
+            top_h2 = []
+            for index_p, p in enumerate(zip(pred2, pred_proba2)):
+                top_h2.append([p[0], np.amax(p[1]), index_p])
+
+            top_h1.sort(key=lambda x: x[1], reverse=True)
+            top_h2.sort(key=lambda x: x[1], reverse=True)
+            top_h1 = np.array(top_h1[:tot])
+            top_h2 = np.array(top_h2[:tot])
+            u1_samples = u1[np.array(top_h1[:, 2], int)]
+            u1_x2_samples = u1[np.array(top_h2[:, 2], int)]
+            u2_samples = u2[np.array(top_h2[:, 2], int)]
+            u2_x1_samples = u2[np.array(top_h1[:, 2], int)]
+
+            u1_new_samples = np.concatenate((u1_samples, u2_x1_samples), axis=1)
+            u2_new_samples = np.concatenate((u2_samples, u1_x2_samples), axis=1)
+            u_new = np.concatenate((u1_new_samples, u2_new_samples))
+            L = np.concatenate((L, u_new))
+            y_new = np.array([x[0] for x in top_h1] + [x[0] for x in top_h2])
+            y = np.concatenate((y, y_new))
+
+            old_indexes = np.array([x[2] for x in top_h1] + [x[2] for x in \
+                                                             top_h2], int)
+            u_prime = np.delete(u_prime, old_indexes, axis=0)
+
+            U = np.delete(U, u_random_index, axis=0)
+            try:
+                u_random_index = rng.choice(len(U),
+                                            size=2 * self.p + 2 * self.n,
+                                            replace=False, shuffle=False)
+            except ValueError:
+                print(f'The model was incorrectly parametrized, k is to big.')
+            try:
+                u_prime = np.concatenate((u_prime, U[u_random_index]))
+            except IndexError:
+                print('The model was incorrectly parametrized, there are not '
+                      'enough unlabeled samples.')
+
+    def predict(self, X):
+        x1, x2 = np.hsplit(X, self.size_x1)
+        pred1, pred_proba1 = self.h1.predict(x1), self.h1.predict_proba(x1)
+        pred2, pred_proba2 = self.h2.predict(x2), self.h2.predict_proba(x2)
+        labels = []
+        for p1, p2, pp1, pp2 in zip(pred1, pred2, pred_proba1, pred_proba2):
+            if p1 == p2:
+                labels.append(p1)
+            elif np.amax(pp1) > np.amax(pp2):
+                labels.append(p1)
+            else:
+                labels.append(p2)
+
+        return np.array(labels)


Test patch:
diff --git a/SemiSupervised Learning Algorithms/testing_driver.py b/SemiSupervised Learning Algorithms/testing_driver.py
new file mode 100644
index 0000000..c7e2296
--- /dev/null
+++ b/SemiSupervised Learning Algorithms/testing_driver.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# -*- coding:utf-8 -*-
+# @Filename:    testing_driver.py
+# @Author:      Daniel Puente Ramírez
+# @Time:        5/1/22 15:54
+
+from sklearn.datasets import load_iris
+from sklearn.metrics import classification_report
+from sklearn.model_selection import train_test_split
+
+from CoTraining import CoTraining
+
+if __name__ == '__main__':
+    model = CoTraining()
+    iris = load_iris()
+    X = iris['data']
+    y = iris['target']
+    X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.2,
+                                                        stratify=y)
+    model.fit(L=X_train, U=X_test, y=y_train)
+    y_pred = model.predict(X_test)
+    print(classification_report(y_test, y_pred))