Introdução
Neste tutorial, aprenderá a utilizar a GUI Libsvm do Scikit-learn, que é uma interface gráfica simples para Libsvm, principalmente destinada a fins didáticos. Pode criar pontos de dados através de cliques e visualizar a região de decisão induzida por diferentes núcleos e definições de parâmetros.
Dicas da Máquina Virtual
Após o arranque da máquina virtual, clique no canto superior esquerdo para mudar para a aba Notebook para aceder ao Jupyter Notebook para praticar.
Por vezes, pode ser necessário esperar alguns segundos para o Jupyter Notebook terminar o carregamento. A validação das operações não pode ser automatizada devido a limitações no Jupyter Notebook.
Se tiver problemas durante o aprendizado, não hesite em contactar o Labby. Forneça feedback após a sessão e resolveremos prontamente o problema para si.
Instalar Pacotes Necessários
Antes de começar, certifique-se de que tem os seguintes pacotes instalados:
- matplotlib
- numpy
- tkinter
- scikit-learn
Pode instalar estes pacotes usando o pip.
Importar Pacotes Necessários
O primeiro passo é importar os pacotes necessários para o projeto.
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as NavigationToolbar2Tk
from matplotlib.figure import Figure
from matplotlib.contour import ContourSet
import sys
import numpy as np
import tkinter as Tk
from sklearn import svm
from sklearn.datasets import dump_svmlight_file
Criar a Classe Modelo
Neste passo, criaremos a classe Modelo que contém os dados. Ela implementa o observável no padrão observador e notifica os observadores registrados sobre eventos de mudança.
class Model:
def __init__(self):
self.observers = []
self.surface = None
self.data = []
self.cls = None
self.surface_type = 0
def changed(self, event):
for observer in self.observers:
observer.update(event, self)
def add_observer(self, observer):
self.observers.append(observer)
def set_surface(self, surface):
self.surface = surface
def dump_svmlight_file(self, file):
data = np.array(self.data)
X = data[:, 0:2]
y = data[:, 2]
dump_svmlight_file(X, y, file)
Criar a Classe Controlador
A classe Controlador é usada para controlar a classe Modelo. Ela contém métodos para ajustar o modelo, adicionar exemplos, limpar os dados e ajustar novamente o modelo.
class Controller:
def __init__(self, model):
self.model = model
self.kernel = Tk.IntVar()
self.surface_type = Tk.IntVar()
self.fitted = False
def fit(self):
train = np.array(self.model.data)
X = train[:, 0:2]
y = train[:, 2]
C = float(self.complexity.get())
gamma = float(self.gamma.get())
coef0 = float(self.coef0.get())
degree = int(self.degree.get())
kernel_map = {0: "linear", 1: "rbf", 2: "poly"}
if len(np.unique(y)) == 1:
clf = svm.OneClassSVM(
kernel=kernel_map[self.kernel.get()],
gamma=gamma,
coef0=coef0,
degree=degree,
)
clf.fit(X)
else:
clf = svm.SVC(
kernel=kernel_map[self.kernel.get()],
C=C,
gamma=gamma,
coef0=coef0,
degree=degree,
)
clf.fit(X, y)
if hasattr(clf, "score"):
print("Precisão:", clf.score(X, y) * 100)
X1, X2, Z = self.decision_surface(clf)
self.model.clf = clf
self.model.set_surface((X1, X2, Z))
self.model.surface_type = self.surface_type.get()
self.fitted = True
self.model.changed("surface")
def decision_surface(self, cls):
delta = 1
x = np.arange(x_min, x_max + delta, delta)
y = np.arange(y_min, y_max + delta, delta)
X1, X2 = np.meshgrid(x, y)
Z = cls.decision_function(np.c_[X1.ravel(), X2.ravel()])
Z = Z.reshape(X1.shape)
return X1, X2, Z
def clear_data(self):
self.model.data = []
self.fitted = False
self.model.changed("clear")
def add_example(self, x, y, label):
self.model.data.append((x, y, label))
self.model.changed("example_added")
self.refit()
def refit(self):
if self.fitted:
self.fit()
Criar a Classe Visualização
A classe Visualização é usada para exibir a interface gráfica do usuário (GUI) e lidar com as interações do usuário.
class View:
def __init__(self, root, controller):
f = Figure()
ax = f.add_subplot(111)
ax.set_xticks([])
ax.set_yticks([])
ax.set_xlim((x_min, x_max))
ax.set_ylim((y_min, y_max))
canvas = FigureCanvasTkAgg(f, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
canvas.mpl_connect("button_press_event", self.onclick)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
self.controllbar = ControllBar(root, controller)
self.f = f
self.ax = ax
self.canvas = canvas
self.controller = controller
self.contours = []
self.c_labels = None
self.plot_kernels()
def plot_kernels(self):
self.ax.text(-50, -60, "Linear: $u^T v$")
self.ax.text(-20, -60, r"RBF: $\exp (-\gamma \| u-v \|^2)$")
self.ax.text(10, -60, r"Poly: $(\gamma \, u^T v + r)^d$")
def onclick(self, event):
if event.xdata and event.ydata:
if event.button == 1:
self.controller.add_example(event.xdata, event.ydata, 1)
elif event.button == 3:
self.controller.add_example(event.xdata, event.ydata, -1)
def update_example(self, model, idx):
x, y, l = model.data[idx]
if l == 1:
color = "w"
elif l == -1:
color = "k"
self.ax.plot([x], [y], "%so" % color, scalex=0.0, scaley=0.0)
def update(self, event, model):
if event == "examples_loaded":
for i in range(len(model.data)):
self.update_example(model, i)
if event == "example_added":
self.update_example(model, -1)
if event == "clear":
self.ax.clear()
self.ax.set_xticks([])
self.ax.set_yticks([])
self.contours = []
self.c_labels = None
self.plot_kernels()
if event == "surface":
self.remove_surface()
self.plot_support_vectors(model.clf.support_vectors_)
self.plot_decision_surface(model.surface, model.surface_type)
self.canvas.draw()
def remove_surface(self):
if len(self.contours) > 0:
for contour in self.contours:
if isinstance(contour, ContourSet):
for lineset in contour.collections:
lineset.remove()
else:
contour.remove()
self.contours = []
def plot_support_vectors(self, support_vectors):
cs = self.ax.scatter(
support_vectors[:, 0],
support_vectors[:, 1],
s=80,
edgecolors="k",
facecolors="none",
)
self.contours.append(cs)
def plot_decision_surface(self, surface, type):
X1, X2, Z = surface
if type == 0:
levels = [-1.0, 0.0, 1.0]
linestyles = ["dashed", "solid", "dashed"]
colors = "k"
self.contours.append(
self.ax.contour(X1, X2, Z, levels, colors=colors, linestyles=linestyles)
)
elif type == 1:
self.contours.append(
self.ax.contourf(
X1, X2, Z, 10, cmap=matplotlib.cm.bone, origin="lower", alpha=0.85
)
)
self.contours.append(
self.ax.contour(X1, X2, Z, [0.0], colors="k", linestyles=["solid"])
)
else:
raise ValueError("tipo de superfície desconhecido")
Criar a Classe ControllBar
A classe ControllBar é usada para controlar as entradas do usuário e exibi-las na GUI.
class ControllBar:
def __init__(self, root, controller):
fm = Tk.Frame(root)
kernel_group = Tk.Frame(fm)
Tk.Radiobutton(
kernel_group,
text="Linear",
variable=controller.kernel,
value=0,
command=controller.refit,
).pack(anchor=Tk.W)
Tk.Radiobutton(
kernel_group,
text="RBF",
variable=controller.kernel,
value=1,
command=controller.refit,
).pack(anchor=Tk.W)
Tk.Radiobutton(
kernel_group,
text="Poly",
variable=controller.kernel,
value=2,
command=controller.refit,
).pack(anchor=Tk.W)
kernel_group.pack(side=Tk.LEFT)
valbox = Tk.Frame(fm)
controller.complexity = Tk.StringVar()
controller.complexity.set("1.0")
c = Tk.Frame(valbox)
Tk.Label(c, text="C:", anchor="e", width=7).pack(side=Tk.LEFT)
Tk.Entry(c, width=6, textvariable=controller.complexity).pack(side=Tk.LEFT)
c.pack()
controller.gamma = Tk.StringVar()
controller.gamma.set("0.01")
g = Tk.Frame(valbox)
Tk.Label(g, text="gamma:", anchor="e", width=7).pack(side=Tk.LEFT)
Tk.Entry(g, width=6, textvariable=controller.gamma).pack(side=Tk.LEFT)
g.pack()
controller.degree = Tk.StringVar()
controller.degree.set("3")
d = Tk.Frame(valbox)
Tk.Label(d, text="degree:", anchor="e", width=7).pack(side=Tk.LEFT)
Tk.Entry(d, width=6, textvariable=controller.degree).pack(side=Tk.LEFT)
d.pack()
controller.coef0 = Tk.StringVar()
controller.coef0.set("0")
r = Tk.Frame(valbox)
Tk.Label(r, text="coef0:", anchor="e", width=7).pack(side=Tk.LEFT)
Tk.Entry(r, width=6, textvariable=controller.coef0).pack(side=Tk.LEFT)
r.pack()
valbox.pack(side=Tk.LEFT)
cmap_group = Tk.Frame(fm)
Tk.Radiobutton(
cmap_group,
text="Hiperplanos", ## Tradução de "Hyperplanes"
variable=controller.surface_type,
value=0,
command=controller.refit,
).pack(anchor=Tk.W)
Tk.Radiobutton(
cmap_group,
text="Superfície", ## Tradução de "Surface"
variable=controller.surface_type,
value=1,
command=controller.refit,
).pack(anchor=Tk.W)
cmap_group.pack(side=Tk.LEFT)
train_button = Tk.Button(fm, text="Ajustar", width=5, command=controller.fit)
train_button.pack()
fm.pack(side=Tk.LEFT)
Tk.Button(fm, text="Limpar", width=5, command=controller.clear_data).pack(
side=Tk.LEFT
)
Criar a Função Principal
A função principal é usada para executar o programa.
def main(argv):
root = Tk.Tk()
model = Model()
controller = Controller(model)
root.wm_title("GUI Libsvm do Scikit-learn")
view = View(root, controller)
model.add_observer(view)
Tk.mainloop()
Executar o Programa
Agora você pode executar o programa chamando a função principal.
if __name__ == "__main__":
main(sys.argv)
Resumo
Neste tutorial, você aprendeu como usar a GUI Libsvm do Scikit-learn para criar pontos de dados por meio de cliques e visualizar a região de decisão induzida por diferentes núcleos e configurações de parâmetros. Você também aprendeu como criar as classes Model, Controller, View e ControllBar, bem como como executar o programa.