#  Machine Learning con Python

## Índice
1. **[¿Qué es Machine Learning?](#1.-¿Qué-es-Machine-Learning?)**

2. **[Tipos de Machine Learning](#2.-Tipos-de-Machine-Learning)**
   * [2.1 Aprendizaje supervisado](#2.1-Aprendizaje-supervisado)
   * [2.2 Aprendizaje no supervisado](#2.2-Aprendizaje-no-supervisado)
   * [2.3 Aprendizaje por refuerzo](#2.3-Aprendizaje-por-refuerzo)

3. **[Sobreentrenamiento y validación](#3.-Sobreentrenamiento-y-validación)**
   * [3.1 ¿Qué es el sobreentrenamiento?](#3.1-¿Qué-es-el-sobreentrenamiento?)
   * [3.2 Cómo evitar el sobreentrenamiento](#3.2-Cómo-evitar-el-sobreentrenamiento)
   * [3.3 Tipos de validaciones cruzadas](#3.3-Tipos-de-validaciones-cruzadas)
       * [3.3.1 Validación cruzada de K iteraciones](#3.3.1-Validación-cruzada-de-K-iteraciones)
       * [3.3.2 Validación cruzada aleatoria](#3.3.2-Validación-cruzada-aleatoria)
       * [3.3.3 Validación cruzada dejando uno fuera](#3.3.3-Validación-cruzada-dejando-uno-fuera)
   * [3.4 Cálculo del error](#3.4-Cálculo-del-error)
       * [3.4.1 Error de la validación cruzada de K iteraciones](#3.4.1-Error-de-la-validación-cruzada-de-K-iteraciones)
       * [3.4.2 Error de la validación cruzada aleatoria](#3.4.2-Error-de-la-validación-cruzada-aleatoria)
       * [3.4.3 Error de la validación cruzada dejando uno fuera](#3.4.3-Error-de-la-validación-cruzada-dejando-uno-fuera)

4. **[Pasos para construir un modelo de machine learning](#4.-Pasos-para-construir-un-modelo-de-machine-learning)**

5. **[Machine learning en Python](#5.-Machine-learning-en-Python)**
   * [5.1 Scikit-Learn](#5.1-Scikit-Learn)
   * [5.2 Statsmodels](#5.2-Statsmodels)
   * [5.3 PyMC](#5.3-PyMC)
   * [5.4 NTLK](#5.4-NTLK)
   * [5.5 Algoritmos más utilizados](#5.5-Algoritmos-más-utilizados)
   * [5.6 Lectura recomedada](#5.6-Lectura-recomedada)

6. **[Support Vector Machines](#6.-Support-Vector-Machines)**
   * [6.1 ¿Qué son las máquinas de vectores de soporte?](#6.1-¿Qué-son-las-máquinas-de-vectores-de-soporte?)
   * [6.2 Espacios inducidos por la función Kernel](#6.2-Espacios-inducidos-por-la-función-Kernel)
       * [6.2.1 Tipos de funciones Kernel (Núcleo)](#6.2.1-Tipos-de-funciones-Kernel)
   * [6.3 Ejemplo de SVM en 2–dimensiones](#6.3-Ejemplo-de-SVM-en-2–dimensiones)
   * [6.4 SVM en Scikit-Learn](#6.4-SVM-en-Scikit-Learn)
       * [6.4.1 Haciendo predicciones](#6.4.1-Haciendo-predicciones)
       * [6.4.2 Classification report](#6.4.2-Classification-report)
   * [6.5 Diferentes Kernels](#6.5-Diferentes-Kernels)

# [1.](#Índice) ¿Qué es Machine Learning?

Una de las ramas de estudio que cada vez está ganando más popularidad dentro de las ciencias de la computación es el * **aprendizaje automático o Machine Learning** *. Muchos de los servicios que utilizamos en nuestro día a día como google, gmail, netflix, spotify o amazon se valen de las herramientas que les brinda el Machine Learning para alcanzar un servicio cada vez más personalizado y lograr así ventajas competitivas sobre sus rivales.

Pero, ¿qué es exactamente Machine Learning?. El Machine Learning es el diseño y estudio de las herramientas informáticas que utilizan la experiencia pasada para tomar decisiones futuras; es el estudio de programas que pueden aprender de los datos. El objetivo fundamental del Machine Learning es generalizar, o inducir una regla desconocida a partir de ejemplos donde esa regla es aplicada. El ejemplo más típico donde podemos ver el uso del Machine Learning es en el filtrado de los correos basura o spam. Mediante la observación de miles de correos electrónicos que han sido marcados previamente como basura, los filtros de spam aprenden a clasificar los mensajes nuevos. El Machine Learning combina conceptos y técnicas de diferentes áreas del conocimiento, como las matemáticas, estadísticas y las ciencias de la computación; por tal motivo, hay muchas maneras de aprender la disciplina.

# [2.](#Índice) Tipos de Machine Learning

El Machine Learning tiene una amplia gama de aplicaciones, incluyendo motores de búsqueda, diagnósticos médicos, detección de fraude en el uso de tarjetas de crédito, análisis del mercado de valores, clasificación de secuencias de ADN, reconocimiento del habla y del lenguaje escrito, juegos y robótica. Pero para poder abordar cada uno de estos temas es crucial en primer lugar distinguir los distintos tipos de problemas de Machine Learning con los que nos podemos encontrar.

## [2.1](#Índice) Aprendizaje supervisado

En los problemas de aprendizaje supervisado se enseña o entrena al algoritmo a partir de datos que ya vienen etiquetados con la respuesta correcta. Cuanto mayor es el conjunto de datos más el algoritmo puede aprender sobre el tema. Una vez concluído el entrenamiento, se le brindan nuevos datos, ya sin las etiquetas de las respuestas correctas, y el algoritmo de aprendizaje utiliza la experiencia pasada que adquirió durante la etapa de entrenamiento para predecir un resultado. Esto es similar al método de aprendizaje que se utiliza en las escuelas, donde se nos enseñan problemas y las formas de resolverlos, para que luego podamos aplicar los mismos métodos en situaciones similares.

## [2.2](#Índice) Aprendizaje no supervisado

En los problemas de aprendizaje no supervisado el algoritmo es entrenado usando un conjunto de datos que no tiene ninguna etiqueta; en este caso, nunca se le dice al algoritmo lo que representan los datos. La idea es que el algoritmo pueda encontrar por si solo patrones que ayuden a entender el conjunto de datos. El aprendizaje no supervisado es similar al método que utilizamos para aprender a hablar cuando somos bebes, en un principio escuchamos hablar a nuestros padres y no entendemos nada; pero a medida que vamos escuchando miles de conversaciones, nuestro cerebro comenzará a formar un modelo sobre cómo funciona el lenguaje y comenzaremos a reconocer patrones y a esperar ciertos sonidos.

## [2.3](#Índice) Aprendizaje por refuerzo

En los problemas de aprendizaje por refuerzo, el algoritmo aprende observando el mundo que le rodea. Su información de entrada es el feedback o retroalimentación que obtiene del mundo exterior como respuesta a sus acciones. Por lo tanto, el sistema aprende a base de ensayo-error. Un buen ejemplo de este tipo de aprendizaje lo podemos encontrar en los juegos, donde vamos probando nuevas estrategias y vamos seleccionando y perfeccionando aquellas que nos ayudan a ganar el juego. A medida que vamos adquiriendo más practica, el efecto acumulativo del refuerzo a nuestras acciones victoriosas terminará creando una estrategia ganadora.

# [3.](#Índice) Sobreentrenamiento y validación

## [3.1](#Índice) ¿Qué es el sobreentrenamiento?

Como mencionamos cuando definimos al Machine Learning, la idea fundamental es encontrar patrones que podamos generalizar para luego poder aplicar esta generalización sobre los casos que todavía no hemos observado y realizar predicciones. Pero también puede ocurrir que durante el entrenamiento solo descubramos casualidades en los datos que se parecen a patrones interesantes, pero que no generalicen. Esto es lo que se conoce con el nombre de **sobreentrenamiento o sobreajuste ó en inglés overfitting** .

El sobreentrenamiento es la tendencia que tienen la mayoría de los algoritmos de Machine Learning a ajustarse a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo que estamos buscando para generalizar. El ejemplo más extremo de un modelo sobreentrenado es un modelo que solo memoriza las respuestas correctas; este modelo al ser utilizado con datos que nunca antes ha visto va a tener un rendimiento azaroso, ya que nunca logró generalizar un patrón para predecir.

    En aprendizaje automático, el sobreajuste (también es frecuente emplear el término en inglés overfitting) es el efecto de sobreentrenar un algoritmo de aprendizaje con unos ciertos datos para los que se conoce el resultado deseado. El algoritmo de aprendizaje debe alcanzar un estado en el que será capaz de predecir el resultado en otros casos a partir de lo aprendido con los datos de entrenamiento, generalizando para poder resolver situaciones distintas a las acaecidas durante el entrenamiento. Sin embargo, cuando un sistema se entrena demasiado (se sobreentrena) o se entrena con datos extraños, el algoritmo de aprendizaje puede quedar ajustado a unas características muy específicas de los datos de entrenamiento que no tienen relación causal con la función objetivo. Durante la fase de sobreajuste el éxito al responder las muestras de entrenamiento sigue incrementándose mientras que su actuación con muestras nuevas va empeorando.

## [3.2](#Índice) Cómo evitar el sobreentrenamiento

Como mencionamos anteriormente, todos los modelos de Machine Learning tienen tendencia al sobreentrenamiento; es por esto que debemos aprender a convivir con el mismo y tratar de tomar medidas preventivas para reducirlo lo más posible. Las dos principales estrategias para lidiar son el sobreentrenamiento son: la retención de datos y la validación cruzada.

En el primer caso, la idea es dividir nuestro conjunto de datos, en uno o varios conjuntos de entrenamiento y otro/s conjuntos de evaluación. Es decir, que no le vamos a pasar todos nuestros datos al algoritmo durante el entrenamiento, sino que vamos a retener una parte de los datos de entrenamiento para realizar una evaluación de la efectividad del modelo. Con esto lo que buscamos es evitar que los mismos datos que usamos para entrenar sean los mismos que utilizamos para evaluar. De esta forma vamos a poder analizar con más precisión como el modelo se va comportando a medida que más lo vamos entrenando y poder detectar el punto crítico en el que el modelo deja de generalizar y comienza a sobreajustarse a los datos de entrenamiento.

**La validación cruzada** es un procedimiento más sofisticado que el anterior. En lugar de solo obtener una simple estimación de la efectividad de la generalización; la idea es realizar un análisis estadístico para obtener otras medidas del rendimiento estimado, como la media y la varianza, y así poder entender cómo se espera que el rendimiento varíe a través de los distintos conjuntos de datos. Esta variación es fundamental para la evaluación de la confianza en la estimación del rendimiento. La validación cruzada también hace un mejor uso de un conjunto de datos limitado; ya que a diferencia de la simple división de los datos en uno el entrenamiento y otro de evaluación; la validación cruzada calcula sus estimaciones sobre todo el conjunto de datos mediante la realización de múltiples divisiones e intercambios sistemáticos entre datos de entrenamiento y datos de evaluación.

La validación cruzada o cross-validation es una técnica utilizada para evaluar los resultados de un análisis estadístico
y garantizar que son independientes de la partición entre datos de entrenamiento y prueba. Consiste en repetir y calcular la media aritmética obtenida de las medidas de evaluación sobre diferentes particiones. Se utiliza en entornos donde el objetivo principal es la predicción y se quiere estimar cómo de preciso es un modelo que se llevará a cabo a la práctica.
Es una técnica muy utilizada en proyectos de inteligencia artificial para validar modelos generados. La siguiente figura muestra un esquema de esta técnica.

## [3.3](#Índice) Tipos de validaciones cruzadas

### [3.3.1](#Índice) Validación cruzada de K iteraciones

En la validación cruzada de K iteraciones o K-fold cross-validation los datos de muestra se dividen en K subconjuntos. Uno de los subconjuntos se utiliza como datos de prueba y el resto (K-1) como datos de entrenamiento. El proceso de validación cruzada es repetido durante k iteraciones, con cada uno de los posibles subconjuntos de datos de prueba. Finalmente se realiza la media aritmética de los resultados de cada iteración para obtener un único resultado. Este método es muy preciso puesto que evaluamos a partir de K combinaciones de datos de entrenamiento y de prueba, pero aun así tiene una desventaja, y es que, a diferencia del método de retención, es lento desde el punto de vista computacional. En la práctica, la elección del número de iteraciones depende de la medida del conjunto de datos. Lo más común es utilizar la validación cruzada de 10 iteraciones (10-fold cross-validation).

### [3.3.2](#Índice) Validación cruzada aleatoria
Este método consiste en dividir aleatoriamente el conjunto de datos de entrenamiento y el conjunto de datos de
prueba. Para cada división la función de aproximación se ajusta a partir de los datos de entrenamiento y calcula los valores de salida para el conjunto de datos de prueba. El resultado final se corresponde a la media aritmética de los valores obtenidos para las diferentes divisiones. La ventaja de este método es que la división de datos entrenamiento-prueba no depende del número de iteraciones. Pero, en cambio, con este método hay algunas muestras que quedan sin evaluar y otras que se evalúan más de una vez, es decir, los subconjuntos de prueba y entrenamiento se pueden solapar.

### [3.3.3](#Índice) Validación cruzada dejando uno fuera
La validación cruzada dejando uno fuera o Leave-one-out cross-validation (LOOCV) implica separar los datos de forma que para cada iteración tengamos una sola muestra para los datos de prueba y todo el resto conformando los datos de entrenamiento. La evaluación viene dada por el error, y en este tipo de validación cruzada el error es muy bajo, pero en cambio, a nivel computacional es muy costoso, puesto que se tienen que realizar un elevado número de iteraciones, tantas como N muestras tengamos y para cada una analizar los datos tanto de entrenamiento como de prueba.

## [3.4](#Índice) Cálculo del error
La evaluación de las diferentes validaciones cruzadas normalmente viene dada por el error obtenido en cada iteración, ahora bien, por cada uno de los métodos puede variar el número de iteraciones, según la elección del diseñador en función del número de datos total.

### [3.4.1](#Índice) Error de la validación cruzada de K iteraciones
En cada una de las k iteraciones de este tipo de validación se realiza un cálculo de error. El resultado final lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{K}\sum_{i=1}^{K}E_i
\end{equation}

Es decir, se realiza el sumatorio de los $K$ valores de error y se divide entre el valor de $K$.

### [3.4.2](#Índice) Error de la validación cruzada aleatoria

En la validación cruzada aleatoria, a diferencia del método anterior, cogemos muestras al azar durante k iteraciones, aunque de igual manera, se realiza un cálculo de error para cada iteración. El resultado final también lo obtenemos a partir de realizar la media aritmética de los $K$ valores de errores obtenidos, según la misma fórmula

### [3.4.3](#Índice) Error de la validación cruzada dejando uno fuera
En la validación cruzada dejando uno fuera se realizan tantas iteraciones como muestras $(N)$ tenga el conjunto de datos. De forma que para cada una de las N iteraciones se realiza un cálculo de error. El resultado final lo obtenemos realizando la media aritmética de los N valores de errores obtenidos, según la fórmula:

\begin{equation}
   E = \frac{1}{N}\sum_{i=1}^{N}E_i
\end{equation}

Donde se realiza el sumatorio de los $N$ valores de error y se divide entre el valor de $N$.

# [4.](#Índice) Pasos para construir un modelo de machine learning

Construir un modelo de Machine Learning, no se reduce solo a utilizar un algoritmo de aprendizaje o utilizar una librería de Machine Learning; sino que es todo un proceso que suele involucrar los siguientes pasos:

* **Recolectar los datos**. Podemos recolectar los datos desde muchas fuentes, podemos, por ejemplo, extraer los datos de un sitio web u obtener los datos utilizando una API o desde una base de datos. Podemos también utilizar otros dispositivos que recolectan los datos por nosotros; o utilizar datos que son de dominio público. El número de opciones que tenemos para recolectar datos ¡no tiene fin!. Este paso parece obvio, pero es uno de los que más complicaciones trae y más tiempo consume.

* **Preprocesar los datos**. Una vez que tenemos los datos, tenemos que asegurarnos que tiene el formato correcto para nutrir nuestro algoritmo de aprendizaje. Es prácticamente inevitable tener que realizar varias tareas de preprocesamiento antes de poder utilizar los datos. Igualmente este punto suele ser mucho más sencillo que el paso anterior.

* **Explorar los datos**. Una vez que ya tenemos los datos y están con el formato correcto, podemos realizar un pre análisis para corregir los casos de valores faltantes o intentar encontrar a simple vista algún patrón en los mismos que nos facilite la construcción del modelo. En esta etapa suelen ser de mucha utilidad las medidas estadísticas y los gráficos en 2 y 3 dimensiones para tener una idea visual de como se comportan nuestros datos. En este punto podemos detectar valores atípicos que debamos descartar; o encontrar las características que más influencia tienen para realizar una predicción.

* **Entrenar el algoritmo**. Aquí es donde comenzamos a utilizar las técnicas de Machine Learning realmente. En esta etapa nutrimos al o los algoritmos de aprendizaje con los datos que venimos procesando en las etapas anteriores. La idea es que los algoritmos puedan extraer información útil de los datos que le pasamos para luego poder hacer predicciones.

* **Evaluar el algoritmo**. En esta etapa ponemos a prueba la información o conocimiento que el algoritmo obtuvo del entrenamiento del paso anterior. Evaluamos que tan preciso es el algoritmo en sus predicciones y si no estamos muy conforme con su rendimiento, podemos volver a la etapa anterior y continuar entrenando el algoritmo cambiando algunos parámetros hasta lograr un rendimiento aceptable.

* **Utilizar el modelo**. En esta ultima etapa, ya ponemos a nuestro modelo a enfrentarse al problema real. Aquí también podemos medir su rendimiento, lo que tal vez nos obligue a revisar todos los pasos anteriores.

# [5.](#Índice) Machine learning en Python

Como siempre me gusta comentar, una de las grandes ventajas que ofrece Python sobre otros lenguajes de programación; es lo grande y prolifera que es la comunidad de desarrolladores que lo rodean; comunidad que ha contribuido con una gran variedad de librerías de primer nivel que extienden la funcionalidades del lenguaje. Para el caso de Machine Learning, las principales librerías que podemos utilizar son:

## [5.1](#Índice) Scikit-Learn

Scikit-learn es la principal librería que existe para trabajar con Machine Learning, incluye la implementación de un gran número de algoritmos de aprendizaje. La podemos utilizar para clasificaciones, extraccion de características, regresiones, agrupaciones, reducción de dimensiones, selección de modelos, o preprocesamiento. Posee una API que es consistente en todos los modelos y se integra muy bien con el resto de los paquetes científicos que ofrece Python. Esta librería también nos facilita las tareas de evaluación, diagnostico y validaciones cruzadas ya que nos proporciona varios métodos de fábrica para poder realizar estas tareas en forma muy simple.

## [5.2](#Índice) Statsmodels

Statsmodels es otra gran librería que hace foco en modelos estadísticos y se utiliza principalmente para análisis predictivos y exploratorios. Al igual que Scikit-learn, también se integra muy bien con el resto de los paquetes cientificos de Python. Si deseamos ajustar modelos lineales, hacer una análisis estadístico, o tal vez un poco de modelado predictivo, entonces Statsmodels es la librería ideal. Las pruebas estadísticas que ofrece son bastante amplias y abarcan tareas de validación para la mayoría de los casos.

## [5.3](#Índice) PyMC

pyMC es un módulo de Python que implementa modelos estadísticos bayesianos, incluyendo la cadena de Markov Monte Carlo(MCMC). pyMC ofrece funcionalidades para hacer el análisis bayesiano lo mas simple posible. Incluye los modelos bayesianos, distribuciones estadísticas y herramientas de diagnostico para la covarianza de los modelos. Si queremos realizar un análisis bayesiano esta es sin duda la librería a utilizar.

## [5.4](#Índice) NTLK

NLTK es la librería líder para el procesamiento del lenguaje natural o NLP por sus siglas en inglés. Proporciona interfaces fáciles de usar a más de 50 cuerpos y recursos léxicos, como WordNet, junto con un conjunto de bibliotecas de procesamiento de texto para la clasificación, tokenización, el etiquetado, el análisis y el razonamiento semántico.

Obviamente, aquí solo estoy listando unas pocas de las muchas librerías que existen en Python para trabajar con problemas de Machine Learning, los invito a realizar su propia investigación sobre el tema.

##  [5.5](#Índice) Algoritmos más utilizados

Los algoritmos que más se suelen utilizar en los problemas de Machine Learning son los siguientes:

 * **Regresión Lineal**
 * **Regresión Logística**
 * **Arboles de Decision**
 * **Random Forest**
 * **SVM o Máquinas de vectores de soporte.**
 * **KNN o K vecinos más cercanos.**
 * **K-means**

## [5.6](#Índice) Lectura recomedada

```python
import warnings

# Arboles de Decision

# importando pandas, numpy y matplotlib
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

```

Cargamos los datos

```python
iris_df = pd.read_csv('../../data/04_01_iris.csv')
iris_df
```

## [7.1](#Índice) Labores de preprocesamiento

Sin embargo, scikit-learn no acepta cadenas como parámetros de las funciones, todo deben de ser números. Para ello, nos podemos valer del objeto sklearn.preprocessing.LabelEncoder, que nos transforma automáticamente las cadenas a números. La forma en que se utiliza es la siguiente:

```python
#? pd.tools.plotting.scatter_matrix()
#? preprocessing.LabelEncoder()

from sklearn import preprocessing
le = preprocessing.LabelEncoder()
le.fit(iris_df['species'])
iris_df['species_cod'] = le.transform(iris_df['species'])

iris_df
#le.inverse_transform(iris_df.species_cod)
```

Como podéis observar, primero se crea el LabelEncoder y luego se "entrena" mediante el método fit. Para un LabelEncoder, "entrenar" el modelo es decidir el mapeo que vimos anteriormente, en este caso:

   * Iris-setosa -> 0
   * Iris-versicolor -> 1
   * Iris-virginica -> 2

Una vez entrenado, utilizando el método transform del LabelEncoder, podremos transformar cualquier ndarray que queramos (hubiéramos tenido un error si alguna de las etiquetas de test no estuviera en train). Esta estructura (método fit más método transform o predict) se repite en muchos de los objetos de scikit-learn.

Hay muchas más tareas de preprocesamiento que se pueden hacer en scikit-learn. **Consulta el paquete sklearn.preprocessing**.

```python
import seaborn as sns;
sns.set(style="ticks", color_codes=True)
iris = sns.load_dataset("iris")
sns.pairplot(iris, hue="species")
plt.show()
# Nota--> ayuda sobre alguna función...
#? sns.pairplot
```

### Separamos los datos

Separamos los datos,

```python
iris_data = iris_df.drop(['species','species_cod'],axis=1)
iris_data.head()
```

```python
iris_target = iris_df.species_cod
iris_target.head()
```

### Separamos los Datos.... Entrenamiento y test

```python
# Separamos los Datos.... Entrenamiento y test
#?  train_test_split()

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris_data, iris_target,
                                                    test_size=0.33,
                                                    random_state=102,
                                                    shuffle =True)

print('Set de datos para Entrenamiento =',len(X_train))
print('Set de datos para Test',len(X_test))
print('Total',len(X_test)+len(X_train))
print('Data Shape=',X_test.shape)
print('Target Shape =',y_test.shape)

X_train.head()
```

# [6.](#Índice) Support Vector Machines

## [6.1](#Índice) ¿Qué son las máquinas de vectores de soporte?

Las máquinas de soporte vectorial, máquinas de vectores de soporte o máquinas de vector soporte (Support Vector Machines, SVMs) son un conjunto de algoritmos de aprendizaje supervisado desarrollados por Vladimir Vapnik y su equipo en los laboratorios AT&T.

Estos métodos están propiamente relacionados con problemas de clasificación y regresión.
El método consiste en que, dado un conjunto de ejemplos de entrenamiento (de muestras) podemos etiquetar las clases y entrenar una SVM para construir un modelo que prediga la clase de una nueva muestra. Intuitivamente, una SVM es un modelo que representa a los puntos de muestra en el espacio, separando las clases a 2 espacios lo más amplios posibles mediante un hiperplano de separación definido como el vector entre los 2 puntos, de las 2 clases, más cercanos al que se llama vector soporte. Cuando las nuevas muestras se ponen en correspondencia con dicho modelo, en función de los espacios a los que pertenezcan, pueden ser clasificadas a una o la otra clase.

    Más formalmente, una SVM construye un hiperplano o conjunto de hiperplanos en un espacio de dimensionalidad muy alta (o incluso infinita) que puede ser utilizado en problemas de clasificación o regresión. Una buena separación entre las clases permitirá una clasificación correcta.

### Idea básica

La SVM busca un hiperplano que separe de forma óptima a los puntos de una clase de la de otra, que eventualmente han podido ser previamente proyectados a un espacio de dimensionalidad superior.

En ese concepto de "separación óptima" es donde reside la característica fundamental de las SVM: este tipo de algoritmos buscan el hiperplano que tenga la máxima distancia (margen) con los puntos que estén más cerca de él mismo. Por eso también a veces se les conoce a las SVM como clasificadores de margen máximo. De esta forma, los puntos del vector que son etiquetados con una categoría estarán a un lado del hiperplano y los casos que se encuentren en la otra categoría estarán al otro lado.

En la literatura de los SVMs, se llama atributo a la variable predictora y característica a un atributo transformado que es usado para definir el hiperplano. La elección de la representación más adecuada del universo estudiado, se realiza mediante un proceso denominado selección de características.

Al vector formado por los puntos más cercanos al hiperplano se le llama vector de soporte.

Los modelos basados en SVMs están estrechamente relacionados con las redes neuronales. Usando una función kernel, resultan un método de entrenamiento alternativo para clasificadores polinomiales, funciones de base radial y perceptrón multicapa.

## [6.2](#Índice) Espacios inducidos por la función Kernel

Debido a las limitaciones computacionales de las máquinas de aprendizaje lineal estas no
pueden ser utilizadas en la mayoría de las aplicaciones del mundo real. La representación por
medio del Kernel ofrece una solución alternativa a este problema, proyectando la información
a un espacio de características de mayor dimensión el cual aumenta la capacidad computacional de la máquinas de aprendizaje lineal. La forma más común en que las máquinas de
aprendizaje lineales aprenden una función objetivo es cambiando la representación de la función, esto es similar a mapear el espacio de entradas $X$ a un nuevo espacio de características $F = \{\phi(x)|x ∈ X\}$. Esto es:

$x = \{ x_1 , x_2,..., x_n \} \rightarrow \phi(x) = \{ \phi(x)_1 , \phi(x)_2 ,..., \phi(x)_n \}$

En la figura siguiente se muestra un mapeo de un espacio de entradas de dos dimensiones a un
espacio de características de dos dimensiones, donde la información no puede ser separada
por una máquina lineal en el espacio de entradas mientras que en el espacio de características
esto resulta muy sencillo.

Definición: Un Kernel $K$ es una función, tal que para todo $x_i,x_j \in X$

\begin{equation}
 K(x_i, x_j) = \langle \phi (x_i) · \phi (x_j) \rangle = \sum_{i=1}^{l} \phi^T_{i}(x_i)\phi_i(x_j)
\end{equation}

donde $\phi$ es un mapeo del espacio de entradas $X$ al espacio de características $F$.
El uso de la función kernel hace posible realizar el mapeo de la información de entrada
$(x_i, x_j)$ al espacio de características $(\phi (x_i) ,  \phi_i (x_j))$ de forma implícita y entrenar a la máquina lineal en dicho espacio.

Luego mediante distintos algoritmos de optimización que permita un margen máximo (distancia) entre los elementos de las dos categorías que nos permite entrenar nuestro modelo.

## [6.2.1](#Índice) Tipos de funciones Kernel

Polinomial-homogénea: $K(x_i, x_j) = (x_i·x_j)^n$

Perceptron: $K(xi, xj)= || xi-xj ||$

Función de base radial Gaussiana: separado por un hiperplano en el espacio transformado. $K(x_i, x_j)=e^{-\frac{(x_i-x_j)^2}{2\sigma^2}}$

Sigmoid: $K(x_i, x_j)=tanh(x_i· x_j−\phi)$

## [6.3](#Índice) Ejemplo de SVM en 2–dimensiones

===========================================================================================

Fig: H1 no separa las clases. H2 las separa, pero solo con un margen pequeño. H3 las separa con el margen máximo.

=============================================================================================

En un ejemplo idealizado para 2-dimensiones, la representación de los datos a clasificar se realiza en el plano x-y. El algoritmo SVM trata de encontrar un hiperplano 1-dimensional (en el ejemplo que nos ocupa es una línea) que une a las variables predictoras y constituye el límite que define si un elemento de entrada pertenece a una categoría o a la otra.

Existe un número infinito de posibles hiperplanos (líneas) que realicen la clasificación pero, ¿cuál es la mejor y cómo la definimos?
Hay infinitos hiperplanos posibles
H1 no separa las clases. H2 las separa, pero solo con un margen pequeño. H3 las separa con el margen máximo.

La mejor solución es aquella que permita un margen máximo entre los elementos de las dos categorías.

Se denominan vectores de soporte a los puntos que conforman las dos líneas paralelas al hiperplano, siendo la distancia entre ellas (margen) la mayor posible.

## [6.4](#Índice) SVM en Scikit-Learn

```python
from sklearn.svm import SVC
model=SVC()
model.fit(X_train,y_train)
```

```python
from sklearn.model_selection import cross_val_score
# ? cross_val_score()
cross_val_score(model, iris_data, iris_target)
model.fit(X_train,y_train)

print ("Score with data Tes",model.score(X_test,y_test))
print ("Score with data Train",model.score(X_train,y_train))
```

###  [6.4.1](#Índice) Haciendo predicciones

```python
warnings.filterwarnings('ignore')

ind = 78
print(iris_data.iloc[ind])
print('specie',iris_target.iloc[ind], le.inverse_transform(iris_target.iloc[[ind]]))
x_new = iris_data.iloc[ind]

print('\n======== PREDICTION ========')
prediction = model.predict([x_new.values])
print('Specie prediction',prediction, le.inverse_transform(prediction))
```

###  [6.4.2](#Índice) Classification Report

```python
from sklearn.metrics import classification_report, accuracy_score, confusion_matrix

p = model.predict(X_test)

print ('Accuracy:', accuracy_score(y_test, p))
print ('\nConfusion Matrix:\n', confusion_matrix(y_test, p))
print ('\nClassification Report:', classification_report(y_test, p))
```

## [6.5](#Índice) Diferentes Kernels

```python
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets

def make_meshgrid(x, y, h=.02):
    """Create a mesh of points to plot in

    Parameters
    ----------
    x: data to base x-axis meshgrid on
    y: data to base y-axis meshgrid on
    h: stepsize for meshgrid, optional

    Returns
    -------
    xx, yy : ndarray
    """
    x_min, x_max = x.min() - 1, x.max() + 1
    y_min, y_max = y.min() - 1, y.max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    return xx, yy

def plot_contours(ax, clf, xx, yy, **params):
    """Plot the decision boundaries for a classifier.

    Parameters
    ----------
    ax: matplotlib axes object
    clf: a classifier
    xx: meshgrid ndarray
    yy: meshgrid ndarray
    params: dictionary of params to pass to contourf, optional
    """
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    out = ax.contourf(xx, yy, Z, **params)
    return out

# import some data to play with
iris = datasets.load_iris()
# Take the first two features. We could avoid this by using a two-dim dataset
X = iris.data[:, :2]
y = iris.target

# we create an instance of SVM and fit out data. We do not scale our
# data since we want to plot the support vectors
C = 0.1  # SVM regularization parameter
models = (svm.SVC(kernel='linear', C=C),
          svm.LinearSVC(C=C),
          svm.SVC(kernel='rbf', gamma=0.7, C=C),
          svm.SVC(kernel='poly', degree=3, C=C))
models = (clf.fit(X, y) for clf in models)

# title for the plots
titles = ('SVC with linear kernel',
          'LinearSVC (linear kernel)',
          'SVC with RBF kernel',
          'SVC with polynomial (degree 3) kernel')

# Set-up 2x2 grid for plotting.
fig, sub = plt.subplots(2, 2)
plt.subplots_adjust(wspace=0.2, hspace=0.2)
plt.rcParams['figure.figsize'] = (20, 16)

X0, X1 = X[:, 0], X[:, 1]
xx, yy = make_meshgrid(X0, X1)

for clf, title, ax in zip(models, titles, sub.flatten()):
    plot_contours(ax, clf, xx, yy,
                  cmap=plt.cm.coolwarm, alpha=0.4)
    ax.scatter(X0, X1, c=y, cmap=plt.cm.coolwarm, s=80, edgecolors='k')
    ax.set_xlim(xx.min(), xx.max())
    ax.set_ylim(yy.min(), yy.max())
    ax.set_xlabel('Sepal length')
    ax.set_ylabel('Sepal width')
    ax.set_xticks(())
    ax.set_yticks(())
    ax.set_title(title)

plt.show()
```

# Ejercicios:

**Replicar estas técnicas de Clasificación con los siguientes DataSet:**

## Breast Cancer Wisconsin (Diagnostic) Data Set

File: ./data/06_breast-cancer-wisconsin-data.csv

Source: http://archive.ics.uci.edu/ml/datasets/breast%2Bcancer%2Bwisconsin%2B%28diagnostic%29

Abstract: Diagnostic Wisconsin Breast Cancer Database Source:

Creators:

    Dr. William H. Wolberg, General Surgery Dept. University of Wisconsin, Clinical Sciences Center Madison, WI 53792 wolberg '@' eagle.surgery.wisc.edu

    W. Nick Street, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 street '@' cs.wisc.edu 608-262-6619

    Olvi L. Mangasarian, Computer Sciences Dept. University of Wisconsin, 1210 West Dayton St., Madison, WI 53706 olvi '@' cs.wisc.edu

Información del conjunto de datos:

Las características se calculan a partir de una imagen digitalizada de aspiración con aguja fina (PAAF) de una masas mamarias. Describen las características de los núcleos celulares presentes en la imagen (más detalles http://pages.cs.wisc.edu/~street/images/)

Attribute Information:

    ID number
    Diagnosis (M = malignant, B = benign)

**Ten real-valued features are computed for each cell nucleus:**

a) radius (mean of distances from center to points on the perimeter)

b) texture (standard deviation of gray-scale values)

c) perimeter

d) area

e) smoothness (local variation in radius lengths)

f) compactness (perimeter^2 / area - 1.0)

g) concavity (severity of concave portions of the contour)

h) concave points (number of concave portions of the contour)

i) symmetry

j) fractal dimension ("coastline approximation" - 1)

## Diabetes Data Set

File: ./data/06_diabetes.csv

Source : https://archive.ics.uci.edu/ml/datasets/diabetes

Michael Kahn, MD, PhD, Universidad de Washington, St. Louis, MO Información del conjunto de datos:

Los registros de pacientes con diabetes se obtuvieron de dos fuentes: a) un dispositivo de registro electrónico automático y b) registros en papel.

El dispositivo automático tenía un reloj interno para los eventos de marca de tiempo, mientras que los registros en papel solo proporcionaban espacios de "tiempo lógico" (desayuno, almuerzo, cena, hora de acostarse).

Para los registros en papel, se asignaron horarios fijos para el desayuno (08:00), el almuerzo (12:00), la cena (18:00) y la hora de acostarse (22:00). Por lo tanto, los registros en papel tienen tiempos de grabación uniformes ficticios, mientras que los registros electrónicos tienen marcas de tiempo más realistas.

Los archivos de diabetes constan de cuatro campos por registro. Cada campo está separado por una pestaña y cada registro está separado por una nueva línea.

Nombres de archivos y formato

(1) Fecha en formato MM-DD-YYYY (2) Tiempo en formato XX: YY (3) Código (4) Valor

El campo Código se descifra de la siguiente manera:

33 = Dosis de insulina regular

34 = dosis de insulina NPH

35 = dosis de insulina UltraLente

48 = medición de glucosa en sangre no especificada

57 = medición de glucosa en sangre no especificada

58 = medición de glucosa en sangre antes del desayuno

59 = medición de glucosa en sangre después del desayuno

60 = medición de glucosa en sangre antes del almuerzo

61 = medición de glucosa en sangre después del almuerzo

62 = medición de glucosa en sangre antes de la cena

63 = medición de glucosa en sangre después de la cena

64 = Medición de glucosa en sangre antes del aperitivo

65 = Síntomas de hipoglucemia

66 = Ingestión típica de comida

67 = Ingestión de comida más de lo habitual

68 = ingesta de comida menos de lo usual

69 = actividad típica de ejercicio

70 = Actividad de ejercicio más de lo habitual

71 = actividad de ejercicio menos de lo habitual

72 = evento especial no especificado


```
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
import pydy

from sympy import symbols
import sympy.physics.mechanics as me
```

```
q1, q2, q3, u1, u2, u3 = me.dynamicsymbols('q1 q2 q3 u1 u2 u3')
q1d, q2d, q3d, u1d, u2d, u3d = me.dynamicsymbols('q1 q2 q3 u1 u2 u3', 1)

r, m, g = symbols('r m g')

# lizard parameters
lB, mB, IBxx = sym.symbols('lB mB IBxx')
lT, mT, ITxx = sym.symbols('lT mT ITxx')

# rotation angles
th1, th2 = me.dynamicsymbols('th1 th2')

# reference frames
N = me.ReferenceFrame('N')
body_f = N.orientnew('body', 'Axis', [q1, N.y])
tail_f = N.orientnew('tail', 'Axis', [q2, N.y])

# articulation point
O = me.Point('C')
O.set_vel(N, 0)

# mass centers
Bo = O.locatenew('B', lB * sym.cos(th1) * body_f.x + lB * sym.sin(th1) * body_f.y)
To = O.locatenew('T', lT * sym.cos(th2) * tail_f.x + lB * sym.sin(th2) * tail_f.y)

# body and tail
body = me.RigidBody('body', Bo, body_f, mB, (me.inertia(body_f, IBxx, IBxx, 0.), Bo))
tail = me.RigidBody('tail', To, tail_f, mB, (me.inertia(tail_f, ITxx, ITxx, 0.), To))

# angular momentum
me.angular_momentum(O, N, body, tail)
```

    IBxx*q1'*body.y + ITxx*q2'*tail.y

```

```

```
N = me.ReferenceFrame('N')
Y = N.orientnew('Y', 'Axis', [q1, N.z])
L = Y.orientnew('L', 'Axis', [q2, Y.x])
R = L.orientnew('R', 'Axis', [q3, L.y])

w_R_N_qd = R.ang_vel_in(N)
R.set_ang_vel(N, u1 * L.x + u2 * L.y + u3 * L.z)
```

```
C = me.Point('C')
C.set_vel(N, 0)
Dmc = C.locatenew('Dmc', r * L.z)
Dmc.v2pt_theory(C, N, R)
```

    r*u2*L.x - r*u1*L.y

```
I = me.inertia(L, m / 4 * r**2, m / 2 * r**2, m / 4 * r**2)
me.mprint(I)
```

    m*r**2/4*(L.x|L.x) + m*r**2/2*(L.y|L.y) + m*r**2/4*(L.z|L.z)

```
kd = [me.dot(R.ang_vel_in(N) - w_R_N_qd, uv) for uv in L]
```

```
ForceList = [(Dmc, -m * g * Y.z)]
BodyD = me.RigidBody('BodyD', Dmc, R, m, (I, Dmc))
BodyList = [BodyD]
```

```
KM = me.KanesMethod(N, q_ind=[q1, q2, q3], u_ind=[u1, u2, u3], kd_eqs=kd)
(fr, frstar) = KM.kanes_equations(ForceList, BodyList)
MM = KM.mass_matrix

forcing = KM.forcing
rhs = MM.inv() * forcing
kdd = KM.kindiffdict()
rhs = rhs.subs(kdd)
rhs.simplify()
```

```
me.mpprint(rhs)
```

    ⎡                              2           ⎤
    ⎢4⋅g⋅sin(q₂) + 6⋅r⋅u₂⋅u₃ - r⋅u₃ (t)⋅tan(q₂)⎥
    ⎢──────────────────────────────────────────⎥
    ⎢                   5⋅r                    ⎥
    ⎢                                          ⎥
    ⎢                -2⋅u₁⋅u₃                  ⎥
    ⎢                ─────────                 ⎥
    ⎢                    3                     ⎥
    ⎢                                          ⎥
    ⎣         (-2⋅u₂ + u₃⋅tan(q₂))⋅u₁          ⎦

```

```


# Load Drivers

The code below links the necessary components to our file. Whether you're attempting to play with the network or train the network, you'll need to run this cell.

```python
#necessary imports
import pandas
import numpy as np
import os
import tensorflow.keras as keras
from keras.models import Model
from keras.layers import Dense, Input
from IPython.display import display
import sympy as sp
sp.init_printing(use_latex = True)
import math

import matplotlib.pyplot as plt
%matplotlib inline

%run exp_Drivers.ipynb

EMPTY = 1;
COLOR = 0;

BLACK = -1;
WHITE = 1;

WIDTH = 9;
```

# Data Categorization and Assignment

If you're training the network, you need to run this code, as it converts the sgf games in the database into positions the network can read.

```python
Boards = []
Moves = []
def Main():
    path = "./go9"
    counter = 0
    for entry in os.scandir(path): #I changed my mind i love python
        Go = True
        Board = createEmptyBoard() # 0 - 80 = [color, empty], 81 = [turn, turn]
        with open(entry) as f:
            if Go:
                for line in f:
                    if line[0] == ';': # this is the line with all the moves.
                        Go = False
                        copy = ""
                        for c in line:
                            if c != "[" and c != "]" and c != ")":
                                copy += c
                        arr = copy[1:].split(';')
                        for a in arr:
                            int_move = Decode_Move(a[1:])
                            move = index_to_coordinate(int_move)
                            color = 1
                            if(a[0] == 'B'):
                                color = -1
                            Boards.append(Board)
                            Moves.append(int_move)
                            if int_move < 81:
                                Board = Move(Board, move[1], move[0])[1]
Main()
Boards = np.array(Boards)
print(Moves[0])
Moves = np.array(Moves)
```

    40

```python
print(Boards.shape)
```

    (414124, 9, 9, 2)

```python
# Example Position:
printBoard(Boards[22], -1)
printBoard(Boards[23], 1)
```

    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O . @ . @ . .
    4  . . . O @ @ O @ .
    5  . . @ O @ O O @ .
    6  . . O . @ O . . .
    7  . . . . @ O . . .
    8  . . . @ O O . . .
    9  . . . . . . . . .

    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O . @ . @ . .
    4  . . . O @ @ O @ .
    5  . . @ O @ O O @ .
    6  . . O @ @ O . . .
    7  . . . . @ O . . .
    8  . . . @ O O . . .
    9  . . . . . . . . .

Create the training and testing data.

```python
X = Boards
Y = keras.utils.to_categorical(Moves)

training_samples = int(0.9 * X.shape[0])
X_train, X_test = X[:training_samples], X[training_samples:] # Inputs
Y_train, Y_test = Y[:training_samples], Y[training_samples:] # Outputs

print(X.shape)
print(Y.shape)
print(Moves)
```

    (414124, 9, 9, 2)
    (414124, 82)
    [40 49 41 ... 75 18 36]

# Building the Model

Here is where the model, a convolutional neural network, is created. The model must be created whether you want to train it, or play against it.

```python

model = keras.models.Sequential()

model.add(keras.layers.Dense(2, activation = 'relu', input_shape = (9, 9, 2)))
model.add(keras.layers.Conv2D(81, (3, 3), activation = 'relu'))
model.add(keras.layers.Conv2D(81, (3, 3), activation = 'relu'))
model.add(keras.layers.Conv2D(81, (3, 3), activation = 'relu'))
model.add(keras.layers.Conv2D(81, (3, 3), activation = 'relu'))
model.add(keras.layers.Flatten())
model.add(keras.layers.Dense(82, activation = 'relu'))
model.add(keras.layers.Dropout(.25))
model.add(keras.layers.Dense(82, activation = 'softmax'))

model.compile(loss=keras.losses.CategoricalCrossentropy(), optimizer = keras.optimizers.Adam(), metrics = [keras.metrics.CategoricalAccuracy()])
print(X_train.shape)

model.summary()

```

    (372711, 9, 9, 2)
    Model: "sequential_24"
    _________________________________________________________________
    Layer (type)                 Output Shape              Param #
    =================================================================
    dense_46 (Dense)             (None, 9, 9, 2)           6
    _________________________________________________________________
    conv2d_82 (Conv2D)           (None, 7, 7, 81)          1539
    _________________________________________________________________
    conv2d_83 (Conv2D)           (None, 5, 5, 81)          59130
    _________________________________________________________________
    conv2d_84 (Conv2D)           (None, 3, 3, 81)          59130
    _________________________________________________________________
    conv2d_85 (Conv2D)           (None, 1, 1, 81)          59130
    _________________________________________________________________
    flatten_13 (Flatten)         (None, 81)                0
    _________________________________________________________________
    dense_47 (Dense)             (None, 82)                6724
    _________________________________________________________________
    dropout_1 (Dropout)          (None, 82)                0
    _________________________________________________________________
    dense_48 (Dense)             (None, 82)                6806
    =================================================================
    Total params: 192,465
    Trainable params: 192,465
    Non-trainable params: 0
    _________________________________________________________________

If you already have a .h5 weights file, then you can run this cell to load those weights.

```python
# Load Weights
model.load_weights('mini_weights.h5')
```

# Training

Here is where the training is conducted. If you simply want to play against the neural net, skip to the last cell.

```python
#Train the model
history = model.fit(X_train, Y_train, batch_size = 32, epochs = 6, workers = 10, verbose = 1, validation_data = (X_test, Y_test))
```

    Epoch 1/6
    182/182 [==============================] - 40s 220ms/step - loss: 2.3604 - categorical_accuracy: 0.3811 - val_loss: 3.2259 - val_categorical_accuracy: 0.2869
    Epoch 2/6
    182/182 [==============================] - 40s 219ms/step - loss: 2.3443 - categorical_accuracy: 0.3834 - val_loss: 3.2210 - val_categorical_accuracy: 0.2867
    Epoch 3/6
    182/182 [==============================] - 40s 218ms/step - loss: 2.3321 - categorical_accuracy: 0.3864 - val_loss: 3.2465 - val_categorical_accuracy: 0.2870
    Epoch 4/6
    182/182 [==============================] - 40s 220ms/step - loss: 2.3212 - categorical_accuracy: 0.3879 - val_loss: 3.2587 - val_categorical_accuracy: 0.2878
    Epoch 5/6
    182/182 [==============================] - 40s 220ms/step - loss: 2.3167 - categorical_accuracy: 0.3897 - val_loss: 3.2685 - val_categorical_accuracy: 0.2867
    Epoch 6/6
    182/182 [==============================] - 40s 221ms/step - loss: 2.3102 - categorical_accuracy: 0.3899 - val_loss: 3.2739 - val_categorical_accuracy: 0.2868

```python
# Save Weights
model.save_weights('mini_weights.h5')
```

# Results

```python
plt.figure(1)

plt.subplot(211)
plt.plot(history.history['categorical_accuracy'])
plt.plot(history.history['val_categorical_accuracy'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')

plt.subplot(212)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('model loss')

plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'val'], loc='upper left')
plt.tight_layout()
plt.show()
```

# Play

```python
Play()
```

    (9, 9, 2)
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . . . . . . .
    4  . . . . . . . . .
    5  . . . . . . . . .
    6  . . . . . . . . .
    7  . . . . . . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 40
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . . . . . . .
    4  . . . . . . . . .
    5  . . . . @ . . . .
    6  . . . . . . . . .
    7  . . . . . . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 38
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . . . . . . .
    4  . . . . . . . . .
    5  . . O . @ . . . .
    6  . . . . . . . . .
    7  . . . . . . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 21
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . @ . . . . .
    4  . . . . . . . . .
    5  . . O . @ . . . .
    6  . . . . . . . . .
    7  . . . . . . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 58
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . @ . . . . .
    4  . . . . . . . . .
    5  . . O . @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 32
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . . @ . . . . .
    4  . . . . . @ . . .
    5  . . O . @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 20
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . . . . .
    4  . . . . . @ . . .
    5  . . O . @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 39
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . . . . .
    4  . . . . . @ . . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 23
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . O . . .
    4  . . . . . @ . . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 33
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . O . . .
    4  . . . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 28
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . O . . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 24
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . . O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 19
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . . . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 51
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . @ . .
    7  . . . . O . . . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    White's Move: : 60
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . @ . .
    7  . . . . O . O . .
    8  . . . . . . . . .
    9  . . . . . . . . .

    Black's Move: : 69
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . @ . .
    7  . . . . O . O . .
    8  . . . . . . @ . .
    9  . . . . . . . . .

    White's Move: : 52
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . . @ O .
    7  . . . . O . O . .
    8  . . . . . . @ . .
    9  . . . . . . . . .

    Black's Move: : 50
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . . . @ . .
    9  . . . . . . . . .

    White's Move: : 70
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . . . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . . . @ O .
    9  . . . . . . . . .

    Black's Move: : 10
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . . . @ O .
    9  . . . . . . . . .

    White's Move: : 67
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . . . .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    Black's Move: : 42
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . @ . .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    White's Move: : 43
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ . .
    4  . O . . . @ @ . .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    Black's Move: : 25
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . . . @ @ . .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    White's Move: : 34
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . . . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    Black's Move: : 30
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . . . . . .

    White's Move: : 76
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O .
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . O . . . .

    Black's Move: : 53
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O @
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . . O . . . .

    White's Move: : 75
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O @
    7  . . . . O . O . .
    8  . . . . O . @ O .
    9  . . . O O . . . .

    Black's Move: : 64
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . . . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    White's Move: : 15
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . . . . . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    Black's Move: : 46
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . . O @ @ . @ O .
    6  . @ . . . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    White's Move: : 37
    #  A B C D E F G H I
    1  . . . . . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ . @ O .
    6  . @ . . . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    Black's Move: : 3
    #  A B C D E F G H I
    1  . . . @ . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ . @ O .
    6  . @ . . . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    White's Move: : 48
    #  A B C D E F G H I
    1  . . . @ . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ . @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    Black's Move: : 41
    #  A B C D E F G H I
    1  . . . @ . . . . .
    2  . @ . . . . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    White's Move: : 13
    #  A B C D E F G H I
    1  . . . @ . . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    Black's Move: : 4
    #  A B C D E F G H I
    1  . . . @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . . O O . . . .

    White's Move: : 74
    #  A B C D E F G H I
    1  . . . @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . .
    8  . @ . . O . @ O .
    9  . . O O O . . . .

    Black's Move: : 62
    #  A B C D E F G H I
    1  . . . @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O . @ O .
    9  . . O O O . . . .

    White's Move: : 73
    #  A B C D E F G H I
    1  . . . @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O . @ O .
    9  . O O O O . . . .

    Black's Move: : 2
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  . @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O . @ O .
    9  . O O O O . . . .

    White's Move: : 45
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O .
    5  . O O @ @ @ @ O .
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O . @ O .
    9  . O O O O . . . .

    Black's Move: : 35
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O @
    5  . O O @ @ @ @ O .
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O . @ O .
    9  . O O O O . . . .

    White's Move: : 68
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O @
    5  . O O @ @ @ @ O .
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O O @ O .
    9  . O O O O . . . .

    Black's Move: : 44
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O O @ O .
    9  . O O O O . . . .

    White's Move: : 71
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ . @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 31
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  . @ . . O O @ O O
    9  . O O O O . . . .

    White's Move: : 63
    #  A B C D E F G H I
    1  . . @ @ @ . . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 5
    #  A B C D E F G H I
    1  . . @ @ @ @ . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  . . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    White's Move: : 54
    #  A B C D E F G H I
    1  . . @ @ @ @ . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 1
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  . @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 9
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  . O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 18
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . . . .

    Black's Move: : 78
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . @ . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O . @ . .

    Black's Move: : 77
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  . O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    Black's Move: : 27
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  . O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    Black's Move: : 36
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ . . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    Black's Move: : 11
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ . .

    Black's Move: : 79
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    White's Move: : 81
    #  A B C D E F G H I
    1  . @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    Black's Move: : 0
    #  A B C D E F G H I
    1  @ @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    White's Move: : 81
    #  A B C D E F G H I
    1  @ @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ .
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    Black's Move: : 26
    #  A B C D E F G H I
    1  @ @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ @
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    White's Move: : 81
    #  A B C D E F G H I
    1  @ @ @ @ @ @ . . .
    2  @ @ @ . O . O . .
    3  @ O O @ . O @ @ @
    4  @ O . @ @ @ @ O @
    5  @ O O @ @ @ @ O @
    6  O @ . O . @ @ O @
    7  O . . . O . O . @
    8  O @ . . O O @ O O
    9  . O O O O @ @ @ .

    Black's Move: : 81
    Players agreed to end the game.

```python

```


# Characterization of Discrete Systems in the Spectral Domain

*This Jupyter notebook is part of a [collection of notebooks](../index.ipynb) in the bachelors module Signals and Systems, Communications Engineering, Universität Rostock. Please direct questions and suggestions to [Sascha.Spors@uni-rostock.de](mailto:Sascha.Spors@uni-rostock.de).*

## Measurement of an Electroacoustic Transfer Function

The propagation of sound from a loudspeaker placed at one position in a room to a microphone placed at another position can be interpreted as system. The system can be regarded as linear time-invariant (LTI) system under the assumption that the [sound pressure level](https://en.wikipedia.org/wiki/Sound_pressure#Sound_pressure_level) is kept reasonably low (e.g. $L_p < 100$ dB, **pay attention to your ears**) and neither the loudspeaker nor the microphone are 'overdriven'. Consequently, its impulse response $h(t)$ characterizes the propagation of sound between theses two positions. Room impulse responses (RIRs) have a wide range of applications in acoustics, e.g. for the characterization of electroacoustic equipment, room acoustics or auralization. Therefore their measurement and modeling by discrete systems has found a lot of attention in digital signal processing.
Similarly, electromagnetic wave radiation between a transmitter and a receiver could be modeled as a linear transmission channel.

The following example demonstrates how an electroacoustic transfer function can be measured and [modeled by an finite impulse response (FIR) system](transfer_function.ipynb#Modeling-a-Continuous-System-by-a-Discrete-System) using the soundcard of a computer. The module [`sounddevice`](http://python-sounddevice.readthedocs.org/) provides access to the soundcard via [`portaudio`](http://www.portaudio.com/). The basic procedure involves the following three steps

1. generation of the measurement signal $x[k]$,
2. playback of measurement signal $x(t)$ and synchronous recording of system response $y(t)$ (digitalization to $y[k]$), and
3. computation of transfer function $H[\mu]$ and impulse response $h[k]$.

```python
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as signal
import sounddevice as sd
%matplotlib inline

fs = 44100  # sampling rate
T = 10  # length of measurement signal in s
Tr = 2  # length of system impulse response in s

t = np.linspace(0, T, T*fs)
```

### Generation of the Measurement Signal

The measurement signal has to fulfill $H[\mu] \neq 0$, as outlined [before](transfer_function.ipynb#Modeling-a-Continuous-System-by-a-Discrete-System). A Dirac impulse $x[k] = \delta[k]$ cannot be used for electroacoustic equipment due to its high [Crest factor](https://en.wikipedia.org/wiki/Crest_factor). Chirp signals show a variety of favourable properties for the measurement of electroacoustic systems, e.g. a low Crest factor and a magnitude spectrum which can be freely adjusted, for example to the background noise.

A sampled linear chirp signal is used as measurement signal

\begin{equation}
x[k] = A \sin \left( \frac{\omega_\text{stop} - \omega_\text{start}}{2 T} \cdot (k T)^2 +  \omega_\text{start} \cdot k T \right)
\end{equation}

where $\omega_\text{start}$ and $\omega_\text{stop}$ denotes its start and end frequency, respectively and $T$ the sampling period. The measurement signal $x[k]$ is generated and normalized

```python
x = signal.chirp(t, 20, T, 20000, 'linear', phi=90)
x = 0.9 * x / np.max(np.abs(x))
```

and its magnitude spectrum $|H[\mu]|$ plotted for illustration

```python
X = np.fft.rfft(x)
f = np.fft.rfftfreq(len(x))*fs

plt.plot(f, 20*np.log10(np.abs(X)))
plt.xlabel(r'$f$ in Hz')
plt.ylabel(r'$|X(f)|$ in dB')
plt.grid()
plt.axis([0, fs/2, 0, 55])
```

    [0, 22050.0, 0, 55]

### Playback of Measurement Signal and Recording of Room Response

The measurement signal $x[k]$ is played through the output of the soundcard and the response $y(t)$ is captured synchronously by the input of the soundcard. The length of the played/captured signal has to be of equal length when using the soundcard. The measurement signal $x[k]$ is zero-padded so that the captured signal $y[k]$ includes the complete system response.

Be sure not to overdrive the speaker and the microphone by keeping the input level well below 0 dBFS.

```python
x = np.concatenate((x, np.zeros(Tr*fs)))
y = sd.playrec(x, fs, channels=1)
sd.wait()
y = np.squeeze(y)

print('Playback level: ', 20*np.log10(max(x)), ' dB')
print('Input level: ', 20*np.log10(max(y)), ' dB')
```

    Playback level:  -0.915149811328209  dB
    Input level:  -2.4185984431271472  dB

### Computation of the Electroacoustic Transfer Function

The acoustic transfer function is computed by driving the spectrum of the system output $Y[\mu] = \text{DFT} \{ y[k] \}$ by the spectrum of the measurement signal $X[\mu] = \text{DFT} \{ x[k] \}$. Since both signals are real-valued, a real-valued fast Fourier transform (FFT) is used.

```python
H = np.fft.rfft(y) / np.fft.rfft(x)
```

The magnitude of the transfer function is plotted for illustration

```python
f = np.fft.rfftfreq(len(x))*fs
plt.plot(f, 20*np.log10(np.abs(H)))
plt.xlabel(r'$f$ in Hz')
plt.ylabel(r'$|H(f)|$ in dB')
plt.grid()
plt.xlim([0, fs/2])
```

    (0, 22050.0)

### Computation of the Electroacoustic Impulse Response

The impulse response is computed by inverse DFT $h[k] = \text{IDFT} \{ H[\mu] \}$ and truncation to its assumed length

```python
h = np.fft.irfft(H)
h = h[0:Tr*fs]
```

It is plotted for illustration

```python
t = 1/fs * np.arange(len(h))
plt.plot(t, h)
plt.xlim([0.1, .2])
plt.xlabel(r'$t$ in s')
plt.ylabel(r'$h[k]$')
plt.grid()
```

**Copyright**

This notebook is provided as [Open Educational Resource](https://en.wikipedia.org/wiki/Open_educational_resources). Feel free to use the notebook for your own purposes. The text is licensed under [Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/), the code of the IPython examples under the [MIT license](https://opensource.org/licenses/MIT). Please attribute the work as follows: *Sascha Spors, Continuous- and Discrete-Time Signals and Systems - Theory and Computational Examples*.


TCII Github

Tarea

Filtros 3er Orden con OTAs

Liaño, Lucas

## Consigna

> El ejercicio consiste en diseñar un filtro que cumpla la plantilla que a continuación se presenta y implementarlo con una topología determinada:
>
>|     |     |
>|----------------|----------|
>| $\alpha_{min}$ | $20dB$   |
>| $\alpha_{max}$ | $0.5dB$  |
>| $f_{p}$   | $1kHz$   |
>| $f_{s}$   | $3.2kHz$ |

# Resolución a Mano

# Simulación circuital

---

## Circuito Normalizado

Ver que cumple que para $f = 159mHz$ (equivalente a $\omega = 1$) se cumple que la transferencia cae $3dB$.

---

## Circuito Desnomalizado

### Valor en $\omega_{p}$

### Valor en $\omega_{B}$

### Valor en $\omega_{s}$

## Implementación Computarizada

En esta sección vamos a encontrar el código de python correspondiente para generar el diagrama de bode y diagrama de polos y ceros para la transferencia del filtro de 3er orden.

#### Importamos las librerias a utilizar

```python
import numpy as np
import matplotlib.pyplot as plt
from splane import pzmap, grpDelay, bodePlot, convert2SOS
from scipy import signal

# Esta es una liberia tomada de la comunidad [https://stackoverflow.com/questions/35304245/multiply-scipy-lti-transfer-functions?newreg=b12c460c179042b09ad75c2fb4297bc9]
from ltisys import *

# Módulos para Jupyter (mejores graficos!)
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['figure.figsize'] = [12, 4]
plt.rcParams['figure.dpi'] = 150 # 200 e.g. is really fine, but slower
```

#### Inicializamos las variables utilizadas

Cargamos los valores de los componentes del circuitos que aparecen en la expresión de la transferencia.

```python
# Constantes del circuito
gm = 212e-6 #212uS
R = 4.7e3   #4.7k
C = 23.8e-9 #23.8nf

# Constantes del filtro
wo = 1/(R3*C)
Q = R2/R3
K = -R3/R1
```

## Definimos la transferencia a partir de la expresión hallada

Consideremos que a partir del diseño nos quedaron las siguientes expresiones:

\begin{equation}
    gm_{1} = gm_{2} = gm_{3} = \dfrac{1}{R}
\end{equation}

\begin{equation}
    C_{1}=C_{2}=C_{3}
\end{equation}

### Transferencia $H_{1}(S)$
$ H_{1}(S) = \dfrac{\frac{gm}{C}}{S + \frac{G}{C}}$
### Transferencia $H_{2}(S)$
$ H_{2}(S) = \dfrac{\frac{gm^2}{C^2}}{s^2 + s\ (\frac{gm}{C}) + \frac{gm^2}{C^2}}$

```python
# Genero la función transferencia H1 en S
num_pasa_bajos = [gm / C]
den_pasa_bajos = [1, 1/(R*C)]
H1 = ltimul(num_pasa_bajos, den_pasa_bajos);

# Genero la función transferencia pasa banda en S
num_pasa_banda = [gm**2 / (C**2)]
den_pasa_banda = [1, gm/C, gm**2 / (C**2)]
H2 = ltimul(num_pasa_banda, den_pasa_banda);
```

## Diagrama de polos y ceros

### Transferencia $H_{1}(S)$

```python
pzmap(H1, 1);
```

### Transferencia $H_{2}(S)$

```python
pzmap(H2, 1);
```

### Transferencia $H(S) = H_{1}(S) \cdot H_{2}(S)$

    Nota: Estamos utilizando una nueva librería (ver en los imports) que permite multiplicar funciones de transferencia.

```python
H = H1 * H2
pzmap(H, 1);
```

## Diagrama de Bode

### Transferencia $H_{1}(S)$

```python
bodePlot(H1.to_ss(), 2);
```

### Transferencia $H_{2}(S)$

```python
bodePlot(H2.to_ss()Transferencia, 2);
```

### Transferencia $H(S) = H_{1}(S) \cdot H_{2}(S)$

```python
bodePlot(H.to_ss(), 2);
```


# メモ

大学で学ぶ線形代数 沢田賢,渡辺展也,安原晃

を読む。

```latex
%%latex
座標平面上の点 ~A=(1,2)~ \\[0.8em]

(a, b)  \\[0.8em]

(a_1,a_2,a_3,a_4,a_5,a_6,a_7,a_8) \\[0.8em]

添え字という。 \\[0.8em]

(a_1,a_2,\cdots, a_n) \\[0.8em]

\begin{matrix}
3 & 10 & 18 & 3\\
1 & 2 & 2 & 8\\
8 & 1 & 8 & 0
\end{matrix} \\[0.8em]

横の並びを行、縦の並びを列と呼ぶ。
```

座標平面上の点 ~A=(1,2)~ \\[0.8em]

(a, b)  \\[0.8em]

(a_1,a_2,a_3,a_4,a_5,a_6,a_7,a_8) \\[0.8em]

添え字という。 \\[0.8em]

(a_1,a_2,\cdots, a_n) \\[0.8em]

\begin{matrix}
3 & 10 & 18 & 3\\
1 & 2 & 2 & 8\\
8 & 1 & 8 & 0
\end{matrix}

```latex
%%latex
行列 \\[0.8em]

\begin{pmatrix}
    a_{11} & a_{12} & \ldots & a_{1n} \\
    a_{21} & a_{22} & \ldots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{m1} & a_{m2} & \ldots & a_{mn}
\end{pmatrix}
```

行列 \\[0.8em]

\begin{pmatrix}
    a_{11} & a_{12} & \ldots & a_{1n} \\
    a_{21} & a_{22} & \ldots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{m1} & a_{m2} & \ldots & a_{mn}
\end{pmatrix}

```latex
%%latex
m \times n~行列という。 成分を~(i,j)~成分という。\\[0.8em]

行列の名前は~A,B,C, \cdots~などのアルファベットの大文字を用いる。
```

m \times n~行列という。 成分を~(i,j)~成分という。\\[0.8em]

行列の名前は~A,B,C, \cdots~などのアルファベットの大文字を用いる。

```latex
%%latex
各成分がすべての~0~の行列を零行列といい、~O~で表す。
\\[0.8em]
\quad \quad \quad \quad
O_{3 \times 4} =
\begin{pmatrix}
    0 & 0 & 0 & 0 \\
    0 & 0 & 0 & 0 \\
    0 & 0 & 0 & 0
\end{pmatrix}

```

各成分がすべての~0~の行列を零行列といい、~O~で表す。
\\[0.8em]
\quad \quad \quad \quad
O_{3 \times 4} =
\begin{pmatrix}
    0 & 0 & 0 & 0 \\
    0 & 0 & 0 & 0 \\
    0 & 0 & 0 & 0
\end{pmatrix}

```latex
%%latex
行の個数と列の個数が同じ行列、つまり~n \times n~行列を~n~次正方行列という。\\[0.8em]

\begin{pmatrix}
    a_{11} & a_{12} & \ldots & a_{1n} \\
    a_{21} & a_{22} & \ldots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \ldots & a_{nn}
\end{pmatrix}\\[0.8em]

において、a_{11},a_{22},\cdots,a_{nn}~を正方行列の対角成分という。
```

行の個数と列の個数が同じ行列、つまり~n \times n~行列を~n~次正方行列という。\\[0.8em]

\begin{pmatrix}
    a_{11} & a_{12} & \ldots & a_{1n} \\
    a_{21} & a_{22} & \ldots & a_{2n} \\
    \vdots & \vdots & \ddots & \vdots \\
    a_{n1} & a_{n2} & \ldots & a_{nn}
\end{pmatrix}\\[0.8em]

において、a_{11},a_{22},\cdots,a_{nn}~を正方行列の対角成分という。

```latex
%%latex
~n~次製法行列で対角成分がすべて~1~、他の成分がすべて~0~となるものを \\
単位行列といい、~E_n~と表す。
\\[0.8em]

E_3 =
\begin{pmatrix}
    1 & 0 & 0 \\
    0 & 1 & 0 \\
    0 & 0 & 1
\end{pmatrix}
```

~n~次製法行列で対角成分がすべて~1~、他の成分がすべて~0~となるものを \\
単位行列といい、~E_n~と表す。
\\[0.8em]

E_3 =
\begin{pmatrix}
    1 & 0 & 0 \\
    0 & 1 & 0 \\
    0 & 0 & 1
\end{pmatrix}

# いまここ p.6

```
from sympy import *

```