<jupyter_start><jupyter_text>IntegrantesAli Villegas Z A01337596 - [Github](https://github.com/AliVillegas/Sistemas-Inteligentes).<NAME> A01114097 - [Github](https://github.com/JulianHerreraH/Sistemas_Inteligentes/tree/dev).<NAME> A01337630 - [Github](https://github.com/alfredoqt/sistemas-inteligentes).  Examen Parcial 1 - Inteligencia Artificial---Inidicaciones: El examen consta de preguntas y ejercicios, las cuales abordan los temas vistos en los Módulos 1 y 2.En la parte teórica, se evaluará la correcta descripción de los términos.Nota: las siguientes preguntas, algunas serán de opción múltiple, otras de Cierto o Falso (y complementando la respuesta) y algunas serán abiertas. En caso de pedir seleccionar la respuesta, marque con ' ** ** ' para hacer negrilla la respuesta(s) seleccionada(s).En la parte práctica, se evalua que el código funcione y se logren los objetivosFecha de liberación del examen: Viernes 11 de septiembre a partir de medio día.Fecha límite de entrega: Viernes 18 de septiembre hasta las 23:59 hrsEsta notebook deberá contener el nombre completo, matrícula y el link correspondiente al GitHub de cada integrante.En caso de no tener la información aquí solicitada, no se evaluará el examen. --- Sección 1 (10 puntos)---  P1.- El campo de la inteligencia artificial solamente trata de entender las entidades inteligentes ¿Cierto o Falso?  ¿Porqué?(Valor: 1 Punto) >**Es FALSO, ya que la inteligencia artificial también busca construir sistemas/entidades inteligentes**  P2.- ¿Qué es la inteligencia artificial?(Valor: 1 Punto) A) Aquella que hace posible que los agentes aprendan, ajustando sus actuarios para que caminen como humanosB) Aquella que hace posible que las máquinas tomen decisiones y desempeñen ejercicios como humanosC) Aquella que hace posible que las máquinas aprendan de la inexperiencia y se ajusten al ambiente**D) Aquella que hace posible que las máquinas aprendan de la experiencia, se ajusten y puedan desempeñar tareas como humanos.**  P3.- ¿Las máquinas ajustan sus respuestas basadas en sus entradas actuales? ¿Cierto o Falso? ¿Porqué?(Valor: 1 Punto) >***Es FALSO, ya que en realidad las máquinas ajustan respuestas en función de los nuevos >datos de entrada; >de esta manera realizan tareas parecidas a los humanos al procesar >cantidades de datos grandes y reconocer patrones en estas***  P4.- La inteligencia artificial se puede clasificar en 4 categorías, selecciona la respuesta correcta:(Valor: 1 Punto) A) Pensar de forma humana, Pensar racionalmente, Actuar racionalmente y Actuar de forma homúncula.B) Pensar de forma humuna, Pensar racionalmente, Actuar racionalmente y Actuar de forma humana.**C) Pensar de forma humana, Pensar racionalmente, Actuar racionalmente y Actuar de forma humana.**D) Pensar de forma humana, Pensar irracionalmente, Actuar racionalmente y Actuar de forma homúncula.  P5.- ¿Cómo es que el humano piensa?(Valor: 1 Punto) >**Los humanos piensan, principlalmente, de tres formas:**  >1. Introspección  >2. Experimentos psicológicos  >3. Imagenes cerebrales  P6.- En el pensamiento racional, ¿qué son los silogismos?(Valor: 1 Punto) >**Es un razonamiento formado por premisas y una conclusión, >que es el resultado lógico que se desprende de las premisas.  Los silogismos proporcionan patrones para las estructuras de los argumentos >que siempre producen conclusiones correctas cuando se proporcionan las premisas correctas.**  P7.- Procesamiento de lenguaje natural, representación del conocimiento, razonamiento automático... son ejemplos de ?(Valor: 1 Punto) >***Imitar cómo actua un humano normal***   P8.- De la acción racional, que se espera que los agentes computacionales realicen?(Valor: 1 Punto) >***Al ser agentes deben cumplir con mas acciones, estas son:***>- Operar autonomamente>- Percibir el ambiente actual>- Persistir por tiempos prolongados>- Adaptarse al cambio >- Crear y perseguir metas definidas  P9.- El aprendizaje no supervisado se puede dividir en 2 categorías de problemas: Agrupamiento y Asociación. ¿Cúal es la diferencia entre ellas?(Valor: 1 Punto) >**La asociación es cuando tenemos varios datos no clasificados y los queremos agrupar, por otro lado>el agrupamiento es cuando los datos ya se encuentran clasificados, pero queremos meter nuevos valores>a los que ya definidos**  P10.- ¿Diferencia entre Inteligencia Artificial y Aprendizaje Máquina?(Valor: 1 Punto) >**La _Inteligencia Artificial_ permite a las máquinas imitar el comportamiento humano. Esto se logra por medio de la precepcion, razonamiento y adaptacion.   > Por otro lado, el _Aprendiza Máquina_ es una rama de la IA, que se enfoca en metodos estadisticos para que las maquinas mejoren con la experiencia** ------ Sección 2 (21 puntos)---  P1.- ¿Qué es un agente?(Valor: 1 Punto) >**_Agente_ es cualquier cosa capaz de percibir su ambiente usando sensores y actuar dentro de este medio a traves de actuadores**  P2.- ¿Diferencia entre agente humano, robótico y de software?(Valor: 1 Punto) >**Estos agentes se desbriben a continuación:**  >- _Agente Humano_ es aquel que tiene oidos, ojos y más organos sensoriales. Usa brazos, manos y boca como actuadores >- _Agente Robótico_ es aquel que puede tener camaras y sensores infrarojos como sensores. Usa motores como actuadores>- _Agente de Software_ es aquel que recibe teclas, archivos e informacion de red como sensores.  Actua en el ambiente por medio de interfaces graficas, archivos o emitiendo paquetes en red  P3.- Menciona que significa PEAS. Proporciona al menos 3 ejemplos de agentes y sus PEAS correspondientes.(Valor: 1 Puntos) **PEAS es in acronimo en ingles que significa: Performance, Environment, Actuadores y Sensores**| Agente                               	| Performance                                        	| Environment                                   	| Actuators                                           	| Sensors                          	||----------------------------------------------	|-----------------------------------------------------	|--------------------------------------------	|-------------------------------------------------	|-----------------------------------	|| Zombie                	| Cerebros comido, hambre  	| Cementerio, Ciudad, Sobrevivientes          	| Brazos, piernas, boca               	| Ojos, oidos            	|| Detector de rostros  	| Detectar individuos, enfocar solo a la cara          	| Aeropuerto, Hospital, Calle  	| Mostrar rostro identificado en pantalla                         	| Camara, IR 	|| Rover de Marte                     	| Exploracion, recoleccion de muestras, imagenes tomadas                    	| Superficie de Marte 	| Llantas, brazos roboticos 	| Camaras, recibidor de radio, GPS, acelerometros    P4.- Menciona las diferencias entre los ambientes completamente y parcialmente observables.(Valor: 1 Punto) >**Es ambiente completamente observable en el que los sensores del agente pueden tienen acceso al estado completo en cada punto del tiempo.  > Por otro lado, el ambiente parcialmente observable, el agente solo tiene datos parciales del estado. Esto puede ser debido a datos ruidosos o no existentes**  P5.- Proporciona 4 ejemplos de tareas en los diferentes tipos de ambientes ambientes (recordemos que hay 10 aproximadamente: Estocástico, Determinista, Secuencial, etc...), considerando lo siguiente: Observable, Tipo de Agente (Sencillo o Múltiple), Tipo de ambiente(Valor: 1 Punto) | Tarea                               	| Observable                                        	| Tipo de Agente                                   	| Tipo de Ambiente                                           	| |----------------------------------------------	|-----------------------------------------------------	|--------------------------------------------	|-------------------------------------------------	|| Rover autonomo                	| Parcialmente 	| Sencillo          	| Estocastico, dinamico, secuencial, continuo               	|| Ajedrez  	| Completo          	| Multiple  	| Deterministico, Statico, Secuencial, Discreto, Desconocido                         	|| Robot de recoge-partes                    	| Parcialmente                    	| Sencillo	| Estocastico, Episodico, Dinamico, Continuo 	| | Solitario(Juego)                   	| Parcialmente                    	| Sencillo	| Deterministico, Episodico, Estatico, Discreto 	|   P6.- ¿Cuáles son las arquitecturas de los agentes?(Valor: 1 Punto) A) Agente de reflejo simple, basado en modelos, basado en obgetivos, basado en utilidad.B) Agente de reflejo multiple, basado en modelos, basado en objetivos, basado en utilidad.**C) Agente de reflejo simple, basado en modelos, basado en objetivos, basado en utilidad.**D) Agente de reflejo simple, basado en modelos, basado en objetivos, basado en utilida.  P7.- Menciona las diferencias entre los 4 agentes anteriores(Valor: 2 Puntos) >**El agente de reflejo simple selecciona acciones basandose en su percepcion actual  > El agente basado en modelos tiene conocimiento de como funciona el mundo, simple o complejo, usa informacion externa> El agente basado en objetivos va mas alla del estado actual, usa metas para describir situaciones favorables. Es mas flexible que los anteriores  > Finalmente, el agente basado en utilidad, usa un modelo de la mano de funciones que de utilidad que miden preferencias. Esto le permite tener una medicion interna que es usada con el desempeño externo para definir acciones**  P8.- ¿Qué son los algoritmos de búsqueda?, ¿cuáles son los elementos que la comprenden?(Valor: 2 Puntos) >**Los _algoritmos de busqueda_ son tecninas para resolver problemas especificos**  > Estan compuestos por los siguientes elementos:> - Estados: Conjunto de todos los posible estados> - Estado(s) inicial(es): donde inicia el algoritmo> - Estado(s) final(es): donde esa la solucion> - Operadores: para pasar entre los estados  P9.- Menciona las diferencias entre búsqueda no informada e informada(Valor: 2 Puntos) >**La busqueda no informada los algoritmos solo tienen el nodo meta como informacion.  Por otro lado, la busqueda informada hace uso de costo en los caminos para saber que estados explorar**  P10.- Describe el algoritmo de Hill Climbing y cómo funciona  de forma general(Valor: 3 Puntos) >**Este algoritmo toma un punto especifico de comienzo en el espacio de busqueda, si encuentra un nuevo punto mejor al presente, lo intercambia.  Si no, se selecciona un vecino para evaluarse. El punto final del algoritmo es cuando no se mejora al intercambiar puntos o se llego el numero maximo definido de interaciones**  P11.- Describe el algoritmo de Recocido Simulado (Simulated Annealing) y cómo funciona  de forma general(Valor: 3 Puntos) >**El SA es un metodo para resolver problemas the optimizacion, la idea es modelar el proceso fisco de calentar un material y despues disminuir su temperatura para reducir defectos.**  >**En cada iteracion el algoritmo selecciona un nuevo punto aleatoriamente, la distancia entre otro punto y el actual es basada en la distribucion de probabilidad determinada por la temperatura. Las temperaturas bajas rechazan soluciones poco favorables y las temperaturas altas aceptan mejores soluciones**  >**A medida que van pasando las iteraciones la temperatura se disminuye, el algoritmo reduce el alcance de la busqueda y se converge en un minimo**  P12.- Describe los algoritmos genéticos y cómo funcionan de forma general(Valor: 3 Puntos) >**Estos algoritmos estan insipirados en la teoria de la evolucion natural. Son usados para problemas de optimizacion y de busqueda**  >**Estos algoritmos modifican su poblacion de soluciones hasta encontrar la mas optima. En cada iteracion seleccion soluciones aleatorias como padres de una nueva generacion, tras multiples iteraciones, se encuentran los sucesores mas optimos**  >**Al estar inspirado en teorias biologicas, usa reglas de seleccion, crossover y mutacion** ------ Sección 3 - Práctico (70 puntos)--- En esta sección se presentan varios códigos donde ustedes deberán completar las línea de código faltantes para que puedan llegar al resultado. ---- P1.- Resolución de un **Sudoku**(Valor: 15 Puntos) Definimos dos posibles tableros para la ejecución de los siguientes códigos, donde se aplican algunos de los algoritmos de búqueda visto en clase.<jupyter_code>import time

# Tablero de prueba
board1 = [
    [1,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,1,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,1,0,0],
    [0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0]
]
board2 = [
    [7,8,0,4,0,0,1,2,0],
    [6,0,0,0,7,5,0,0,9],
    [0,0,0,6,0,1,0,7,8],
    [0,0,7,0,4,0,2,6,0],
    [0,0,1,0,5,0,9,3,0],
    [9,0,4,0,6,0,0,0,5],
    [0,7,0,3,0,0,0,1,2],
    [1,2,0,0,0,7,4,0,0],
    [0,4,9,2,0,6,0,0,7]
]<jupyter_output><empty_output><jupyter_text>A partir de las siguientes secciones, deberás completar las líneas faltantes para poder llegar al resultado esperado.<jupyter_code>#Necesitamos definir nuestra función de impresión
def print_Table(sudoku):
    for i in range(len(sudoku)):
        if i % 3 == 0 and i != 0:
            print("- - - - - - - - - - - - - ")
        for j  in range(len(sudoku[i])):
            if j % 3 == 0 and j != 0:
                print(" | ", end="")
            if j == 8:
                print(sudoku[i][j])
            else:
                print(str(sudoku[i][j]) + " ", end="")
print_Table(board1) # para testear print<jupyter_output>1 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
- - - - - - - - - - - - - 
0 0 0  | 1 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
- - - - - - - - - - - - - 
0 0 0  | 0 0 0  | 1 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0<jupyter_text>De primera instancia, debe existir una función para corroborar que el dato actual sea un 0 para tomar una decisión<jupyter_code>#Así como el código correspondiente para buscar los 0's definidos
def empty_Check(sudoku):
    for i in range(len(sudoku)):
        for j in range(len(sudoku[i])):
            if sudoku[i][j] == 0:
                return (i, j)  # row, col
    return None<jupyter_output><empty_output><jupyter_text>P2.- Describe el funcionamiento de **Algorithm**.Primero con check empty vemos espacios vacios y luego probamos con diferentes digitos en ese espacio,luego se intenta llenar ese cuadro con valores válidos, si algún valor hace que el bloque no se resuelva entonces se resetea ese bloque de 3x3. y se prueba con nuevos digitos. Entonces lo sigue probando hasta que todo el tablero tiene solución. Suponemos que es backtracking por eso mismo de que en cuanto checa que ya no es válido se regresa y prueba con los siguientes valores. (Valor: 10 puntos extra)<jupyter_code>def solving(sudoku):
    check = empty_Check(sudoku)
    if not check:
        return True
    else:
        row, col = check

    for i in range(1,10):
        if algorithm(sudoku, i, (row, col)):
            sudoku[row][col] = i
            if solving(sudoku):
                return True

            sudoku[row][col] = 0

    return False
def algorithm(sudoku, num, pos):
    for i in range(len(sudoku[0])):
        if sudoku[pos[0]][i] == num and pos[1] != i:
            return False

    for i in range(len(sudoku)):
        if sudoku[i][pos[1]] == num and pos[0] != i:
            return False

    # Check box
    box_x = pos[1] // 3
    box_y = pos[0] // 3

    for i in range(box_y*3, box_y*3 + 3):
        for j in range(box_x * 3, box_x*3 + 3):
            if sudoku[i][j] == num and (i,j) != pos:
                return False
    return True<jupyter_output><empty_output><jupyter_text>A continuación, deberás imprimir el tablero inicial, llavar el método correspondiente y de la misma forma, imprimir el resultado.<jupyter_code>print("Original Board")
print_Table(board1)
print("Solution Board1")
start_time = time.time() #para comparar performance con DFS y BFS 
solving(board1)
elapsed_time = time.time() - start_time  
print ("Elapsed time: " + str(elapsed_time))

print_Table(board1)
print("___________________")
print("\n")
print("Original Board 2")
print_Table(board2)
print("\n")

print("Solution Board2")
start_time = time.time() #para comparar performance con DFS y BFS 
solving(board2)
elapsed_time = time.time() - start_time  
print_Table(board2)
print ("Elapsed time: " + str(elapsed_time))

# caso de prueba
boardPrueba = [
[5,0,0, 0,8,3, 0,0,2 ],
[0,0,0, 7,0,2, 8,0,0],
[0,0,6, 0,0,0, 3,0,9],
[0,0,0, 4,0,8, 0,1,0],
[0,0,2, 9,3,7, 5,0,0],
[0,6,0, 2,0,1, 0,0,0],
[3,0,7, 0,0,0, 6,0,0],
[0,0,8, 3,0,5, 0,0,0],
[9,0,0, 8,4,0, 0,0,5],
]

print("___________________")
print("\n")
print("Original Board Test")
print_Table(boardPrueba)
print("\n")

print("Solution BoardTest")
start_time = time.time() #para comparar performance con DFS y BFS 
solving(boardPrueba)
elapsed_time = time.time() - start_time  
print_Table(boardPrueba)
print ("Elapsed time: " + str(elapsed_time))<jupyter_output>Original Board
1 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
- - - - - - - - - - - - - 
0 0 0  | 1 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
- - - - - - - - - - - - - 
0 0 0  | 0 0 0  | 1 0 0
0 0 0  | 0 0 0  | 0 0 0
0 0 0  | 0 0 0  | 0 0 0
Solution Board1
Elapsed time: 0.004487276077270508
1 2 3  | 4 5 6  | 7 8 9
4 5 6  | 7 8 9  | 2 1 3
7 8 9  | 2 1 3  | 4 5 6
- - - - - - - - - - - - - 
2 3 4  | 1 6 5  | 8 9 7
5 1 7  | 8 9 2  | 3 6 4
6 9 8  | 3 4 7  | 5 2 1
- - - - - - - - - - - - - 
3 6 2  | 9 7 8  | 1 4 5
8 4 5  | 6 3 1  | 9 7 2
9 7 1  | 5 2 4  | 6 3 8
___________________


Original Board 2
7 8 0  | 4 0 0  | 1 2 0
6 0 0  | 0 7 5  | 0 0 9
0 0 0  | 6 0 1  | 0 7 8
- - - - - - - - - - - - - 
0 0 7  | 0 4 0  | 2 6 0
0 0 1  | 0 5 0  | 9 3 0
9 0 4  | 0 6 0  | 0 0 5
- - - - - - - - - - - - - 
0 7 0  | 3 0 0  | 0 1 2
1 2 0  | 0 0 7  | 4 0 0
0 4 9  | 2 0 6  | 0 0 7


Solution Board2
7 8 5  | 4 3 9  | 1 2 6
6 1 2  | 8 7 5  | 3 4 9
4 9 3  | 6 2 1  | 5[...]<jupyter_text>---En este apartado retomaremos la idea de los agentes inteligentes con las clásicas definiciones de `Nodo`, `Problema` y dos métodos de búsqueda: `BFS`y `DFS`  P3.- Completa los espacios vacios en los códigos(Valor: 10 puntos)<jupyter_code>class Node:
    def __init__(self, state, action=None):
        self.state = state
        self.action = action
    # Use each action to create a new board state
    def expand(self, problem):
        return [self.child_node(problem, action)
                for action in problem.actions(self.state)]
    # Return node with new board state
    def child_node(self, problem, action):
        next = problem.result(self.state, action)
        return Node(next, action)
from queue import Queue
import copy
import time

class Problem(object):

    def __init__(self, initial):
        self.initial = initial
        self.type = len(initial)
        self.height = int(self.type/3)

    # Return set of valid numbers from values that do not appear in used
    def filter_values(self, values, used):
        return [number for number in values if number not in used]

    # Return first empty spot on grid (marked with 0)
    # Si board es un int que viene del length de nuestro 2darray
    #entonces no se puede loopear, por eso lo cambiamos a in range of (board)
    def get_spot(self, board, state):
        for row in range(board): 
            for column in range(board):
                if state[row][column] == 0:
                    return row, column   

    def actions(self, state):
        number_set = range(1, self.type+1) # Defines set of valid numbers that can be placed on board
        in_column = [] # List of valid values in spot's column
        in_block = [] # List of valid values in spot's quadrant

        row,column = self.get_spot(self.type, state) # Get first empty spot on board

        # Filter valid values based on row
        in_row = [number for number in state[row] if (number != 0)]
        options = self.filter_values(number_set, in_row)

        # Filter valid values based on column
        for column_index in range(self.type):
            if state[column_index][column] != 0:
                in_column.append(state[column_index][column])
        options = self.filter_values(options, in_column)

        # Filter with valid values based on quadrant
        row_start = int(row/self.height)*self.height
        column_start = int(column/3)*3
        
        for block_row in range(0, self.height):
            for block_column in range(0,3):
                in_block.append(state[row_start + block_row][column_start + block_column])
        options = self.filter_values(options, in_block)

        for number in options:
            yield number, row, column      

    # Returns updated board after adding new valid value
    def result(self, state, action):

        play = action[0]
        row = action[1]
        column = action[2]

        # Add new valid value to board
        new_state = copy.deepcopy(state)
        new_state[row][column] = play

        return new_state

    # Use sums of each row, column and quadrant to determine validity of board state
    def goal_test(self, state):

        # Expected sum of each row, column or quadrant.
        total = sum(range(1, self.type+1))

        # Check rows and columns and return false if total is invalid
        for row in range(self.type):
            if (len(state[row]) != self.type) or (sum(state[row]) != total):
                return False

            column_total = 0
            for column in range(self.type):
                column_total += state[column][row]

            if (column_total != total):
                return False

        # Check quadrants and return false if total is invalid
        for column in range(0,self.type,3):
            for row in range(0,self.type,self.height):

                block_total = 0
                for block_row in range(0,self.height):
                    for block_column in range(0,3):
                        block_total += state[row + block_row][column + block_column]

                if (block_total != total):
                    return False

        return True
from collections import deque

def BFS(problem):
    start = Node(problem.initial)
    if problem.goal_test(start.state):
        return start
    frontier = Queue() 
    frontier.put(start)  #como estamos trabajando con queue, podemos simplemente meter start, sin tener que manejar cada estado 
    while (frontier.qsize() != 0):
        node = frontier.get()
        for child in node.expand(problem):
            if problem.goal_test(child.state):
                return child
            frontier.put(child)
    return None
def DFS(problem):
    start = Node(problem.initial)
    if problem.goal_test(start.state):
        return start.state
    stack = []
    stack.append(start)
    while stack:
        node = stack.pop()
        if problem.goal_test(node.state):
            return node
        stack.extend(node.expand(problem))
    return None<jupyter_output><empty_output><jupyter_text>Imprime los resultados y el tiempo de ejecución de BFS y DFS<jupyter_code>print ("Problem:")
board2 = [ 
[7, 8, 0, 4, 0, 0, 1, 2, 0],
[6, 0, 0, 0, 7, 5, 0, 0, 9],
[0, 0, 0, 6, 0, 1, 0, 7, 8],
[0, 0, 7, 0, 4, 0, 2, 6, 0],
[0, 0, 1, 0, 5, 0, 9, 3, 0],
[9, 0, 4, 0, 6, 0, 0, 0, 5],
[0, 7, 0, 3, 0, 0, 0, 1, 2],
[1, 2, 0, 0, 0, 7, 4, 0, 0],
[0, 4, 9, 2, 0, 6, 0, 0, 7],
]
for row in board2:
    print(*row)

print ("\nSolving with BFS...")
problem = Problem(board2)
start_time = time.time() #usamos time para sacar el tiempo de la search
bfs = BFS(problem)
elapsed_time = time.time() - start_time  
if bfs != None: 
    print ("Found solution")
    for row in bfs.state:
        print(*row)
else:
    print ("No possible solutions")
print ("Elapsed time: " + str(elapsed_time))

# probamos con otro tablero para comprobar

print ("Problem:")
board2 = [ 
[7, 8, 0, 4, 0, 0, 1, 2, 0],
[6, 0, 0, 0, 7, 5, 0, 0, 9],
[0, 0, 0, 6, 0, 1, 0, 7, 8],
[0, 0, 7, 0, 4, 0, 2, 6, 0],
[0, 0, 1, 0, 5, 0, 9, 3, 0],
[9, 0, 4, 0, 6, 0, 0, 0, 5],
[0, 7, 0, 3, 0, 0, 0, 1, 2],
[1, 2, 0, 0, 0, 7, 4, 0, 0],
[0, 4, 9, 2, 0, 6, 0, 0, 7],
]
for row in board2:
    print(*row) 
print ("\nSolving with DFS...")
problem = Problem(board2)
start_time = time.time() #usamos time para sacar el tiempo de la search
dfs = DFS(problem)
elapsed_time = time.time() - start_time  
if dfs != None: 
    print ("Found solution")
    for row in dfs.state:
        print(*row)
else:
    print ("No possible solutions")
print ("Elapsed time: " + str(elapsed_time))<jupyter_output>Problem:
7 8 0 4 0 0 1 2 0
6 0 0 0 7 5 0 0 9
0 0 0 6 0 1 0 7 8
0 0 7 0 4 0 2 6 0
0 0 1 0 5 0 9 3 0
9 0 4 0 6 0 0 0 5
0 7 0 3 0 0 0 1 2
1 2 0 0 0 7 4 0 0
0 4 9 2 0 6 0 0 7

Solving with DFS...
Found solution
7 8 5 4 3 9 1 2 6
6 1 2 8 7 5 3 4 9
4 9 3 6 2 1 5 7 8
8 5 7 9 4 3 2 6 1
2 6 1 7 5 8 9 3 4
9 3 4 1 6 2 7 8 5
5 7 8 3 9 4 6 1 2
1 2 6 5 8 7 4 9 3
3 4 9 2 1 6 8 5 7
Elapsed time: 0.006944179534912109<jupyter_text>P4.- El siguiente algoritmo es un ejemplo de la implementación de `Hill Climbing` y `Simulated Annealing `, deberás completar las líneas faltantes(Valor: 30 puntos)<jupyter_code>## Throughout this program we have:
##   r is a row,    e.g. 'A'
##   c is a column, e.g. '3'
##   s is a square, e.g. 'A3'
##   d is a digit,  e.g. '9'
##   u is a unit,   e.g. ['A1','B1','C1','D1','E1','F1','G1','H1','I1']
##   grid is a grid,e.g. 81 non-blank chars, e.g. starting with '.18...7...
##   values is a dict of possible values, e.g. {'A1':'12349', 'A2':'8', ...}

from random import randrange
import math
import itertools

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

digits   = '123456789'
rows     = 'ABCDEFGHI'
cols     = digits
squares  = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])
units = dict((s, [u for u in unitlist if s in u])
             for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s]))
             for s in squares)

columns = [cross(rows, c) for c in cols]
lines = [cross(r, cols) for r in rows]
boxes = dict((s, set(sum([units[s][2]],[]))-set([s])) for s in squares)

#compteur du nombre d'essaie
try_counter = 0

################ Parse a Grid ################
def parse_grid(grid):
    """Convert grid to a dict of possible values, {square: digits}, or
    return False if a contradiction is detected."""
    ## To start, every square can be any digit; then assign values from the grid.
    values = dict((s, digits) for s in squares)
    for s,d in grid_values(grid).items():
        if d in digits and not assign(values, s, d):
            return False ## (Fail if we can't assign d to square s.)
    return values

def grid_values(grid):
    "Convert grid into a dict of {square: char} with '0' or '.' for empties."
    chars = [c for c in grid if c in digits or c in '0.']
    assert len(chars) == 81
    return dict(zip(squares, chars))

################ Constraint Propagation ################
def assign(values, s, d):
    """Eliminate all the other values (except d) from values[s] and propagate.
    Return values, except return False if a contradiction is detected."""
    global try_counter
    try_counter += 1
    other_values = values[s].replace(d, '')
    if all(eliminate(values, s, d2) for d2 in other_values):
        return values
    else:
        return False

def eliminate(values, s, d):
    """Eliminate d from values[s]; propagate when values or places <= 2.
    Return values, except return False if a contradiction is detected."""
    if d not in values[s]:
        return values ## Already eliminated
    values[s] = values[s].replace(d,'')
    ## (1) If a square s is reduced to one value d2, then eliminate d2 from the peers.
    if len(values[s]) == 0:
        return False ## Contradiction: removed last value
    elif len(values[s]) == 1:
        d2 = values[s]
        if not all(eliminate(values, s2, d2) for s2 in peers[s]):
            return False
    ## (2) If a unit u is reduced to only one place for a value d, then put it there.
    for u in units[s]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False ## Contradiction: no place for this value
        elif len(dplaces) == 1:
            # d can only be in one place in unit; assign it there
            if not assign(values, dplaces[0], d):
                return False
    return values


############### Constraint propagation (in the boxes) for initialization of Hill Climbing ########################
def assign_HC(values, s, d):
    """Eliminate all the other values (except d) from values[s] and propagate in the box.
    Return values, except return False if a contradiction is detected in the box."""
    other_values = values[s].replace(d, '')
    if all(eliminate_HC(values, s, d2) for d2 in other_values):
        return values
    else:
        return False

def eliminate_HC(values, s, d):
    """Eliminate d from values[s]; propagate when values or places <= 2.
    Return values, except return False if a contradiction is detected."""
    if d not in values[s]:
        return values ## Already eliminated
    values[s] = values[s].replace(d,'')
    ## (1) If a square s is reduced to one value d2, then eliminate d2 from the box.
    if len(values[s]) == 0:
        return False ## Contradiction: removed last value
    elif len(values[s]) == 1:
        d2 = values[s]
        if not all(eliminate_HC(values, s2, d2) for s2 in boxes[s]):
            return False
    ## (2) If a unit u is reduced to only one place for a value d, then put it there.
    for u in [units[s][2]]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False ## Contradiction: no place for this value
        elif len(dplaces) == 1:
            # d can only be in one place in unit; assign it there
            if not assign_HC(values, dplaces[0], d):
                return False
    return values

################ Display as 2-D grid ################
def display(values):
    "Display these values as a 2-D grid."
    width = 1+max(len(values[s]) for s in squares)
    line = '+'.join(['-'*(width*3)]*3)
    for r in rows:
        print(''.join(values[r + c].center(width) + ('|' if c in '36' else '')
                      for c in cols))
        if r in 'CF': print(line)
    print()

################ Search ################
def solve(grid):
    #met le compteur a zero a chaque nouvelle grille de sudoku
    global try_counter
    try_counter = 0
    return search(parse_grid(grid))

def search(values):
    "Using depth-first search and propagation, try all possible values."
    if values is False:
        return False ## Failed earlier
    if all(len(values[s]) == 1 for s in squares):
        return values ## Solved!
    ## Chose the unfilled square s with the fewest possibilities
    n,s = min((len(values[s]), s) for s in squares if len(values[s]) > 1)
    return some(search(assign(values.copy(), s, d))
                for d in values[s])


########################### Hill Climbing ##################################333333
def solve_hill_climbing(grid):
    #met le compteur a zero a chaque nouvelle grille de sudoku
    global try_counter
    try_counter = 0
    values = parse_grid(grid)
    return hill_climbing(initialize_hill_climbing(values))

def initialize_hill_climbing(values):
    "En tenant compte seulement des boites, remplit chaque carre, avec au hasard, un des chiffre possibles"
    new_values = values.copy()
    #tant quil y a un carre vide
    while max(len(new_values[s]) for s in squares) > 1:
        ## Chose the unfilled square s with the fewest possibilities
        n, s = min((len(new_values[s]), s) for s in squares if len(new_values[s]) > 1)
        random_index = randrange(0, len(new_values[s]))
        d = new_values[s][random_index]
        assign_HC(new_values, s, d)
    return new_values

def hill_climbing(values):
    "Using Hill-Climbing, try to find a solution"
    currentNode = values
    while(True):
        L = neighbors(currentNode)
        nextEval = float("-inf")
        nextNode = None
        for x in L:
            if (evaluation(x) > nextEval):
                nextNode = x
                nextEval = evaluation(x)
        if nextEval <= evaluation(currentNode):
            #Return current node since no better neighbors exist
            return currentNode
        #print(evaluation(nextNode))
        currentNode = nextNode

        global try_counter
        try_counter += 1

def neighbors(currentNode):
    """retourne une liste des voisins du noeuds
    les voisins sont les noeuds obtenus en echangeant les chiffres de deux carres appartenant a la meme boite (36 possibilites par boite)"""
    neighbors = []
    done = []
    for s in squares:
        newNode = currentNode.copy()
        box = boxes[s] - set(done)
        for s2 in box:
            newNode[s], newNode[s2] = newNode[s2], newNode[s]
            neighbors.append(newNode)
        done.append(s)
    return neighbors

def evaluation(values):
    # l'evaluation est egal a: 0 - nb de conflit sur les lignes et colonnes
    conflicts = 0
    for line in lines:
        l = []
        for s in line:
            l.append(values[s])
        conflicts += len(set(l)) - 9
    for column in columns:
        c = []
        for s in column:
            c.append(values[s])
        conflicts += len(set(c)) - 9
    return conflicts


############### Simulated annealing ##############
def solve_simulated_annealing(grid):
    #met le compteur a zero a chaque nouvelle grille de sudoku
    global try_counter
    try_counter = 0
    values = parse_grid(grid)
    return simulated_annealing(initialize_hill_climbing(values))

def simulated_annealing(values):
    global try_counter
    current = values
    t = 1
    alpha = 0.99
    for i in range(10000):
        t = alpha * t
        if t == 0:
            return current
        if solved(current):
            return current
        next = random_neighbor(current)
        deltaE = evaluation(next) - evaluation(current)
        if deltaE > 0:
            current = next
            try_counter += 1
        elif jump(probability=math.exp(deltaE / t)):
            current = next
            try_counter += 1
    return values #return current values if not solved after trying 10k neighbors

def random_neighbor(current):
    newNode = current.copy()
    s = random.choice(squares)
    s2 = random.sample(boxes[s],1)[0]
    newNode[s], newNode[s2] = newNode[s2], newNode[s]
    return newNode

def jump(probability):
    return random.random() < probability

################ Utilities ################
def some(seq):
    "Return some element of seq that is true."
    for e in seq:
        if e: return e
    return False

def from_file(filename, sep='\n'):
    "Parse a file into a list of strings, separated by sep."
    return open(filename).read().strip().split(sep)

def shuffled(seq):
    "Return a randomly shuffled copy of the input sequence."
    seq = list(seq)
    random.shuffle(seq)
    return seq

################ System test ################
import time, random, numpy
def solve_all(grids, name='', showif=0.0):
    def time_solve(grid):
        start_time = time.time()
        if name == 'hc':
            values = solve_hill_climbing(grid)
        elif name == 'sa':
            values = solve_simulated_annealing(grid)

        t = time.time() - start_time
        global try_counter
        ## Display puzzles that take long enough
        if showif is not None and t > showif:
            display(grid_values(grid))
            if values: display(values)
            print('(%.2f seconds)\n' % t)
        return (t, solved(values), try_counter)
    times, results, visited_nodes = zip(*[time_solve(grid) for grid in grids])
    N = len(grids)
    if N >= 1:
        if N == 100:
            print("%s \n Solved: %.2f%s \n Times (min: %.2f, avg: %.2f, max: %.2f, std dev.: %.4f) \n Visited nodes (min: %d, avg: %.2f, max: %d, std dev.: %.4f)" % (
                name, sum(results), '%', min(times), sum(times)/N, max(times), numpy.std(times,ddof=1), min(visited_nodes), sum(visited_nodes)/N, max(visited_nodes), numpy.std(visited_nodes,ddof=1)))
        else:
            print("%s \n Solved: %d/%d \n Times (min: %.2f, avg: %.2f, max: %.2f, std dev.: %.4f) \n Visited nodes (min: %d, avg: %.2f, max: %d, std dev.: %.4f)" % (
                name, sum(results), N, min(times), sum(times) / N, max(times), numpy.std(times, ddof=1),
                min(visited_nodes), sum(visited_nodes) / N, max(visited_nodes), numpy.std(visited_nodes, ddof=1)))


def solved(values):
    "A puzzle is solved if each unit is a permutation of the digits 1 to 9."
    def unitsolved(unit): return set(values[s] for s in unit) == set(digits)
    return values is not False and all(unitsolved(unit) for unit in unitlist)

grid1  = '003020600900305001001806400008102900700000008006708200002609500800203009005010300'
grid2  = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......'
hard1  = '.....6....59.....82....8....45........3........6..3.54...325..6..................'<jupyter_output><empty_output><jupyter_text>Usando el código anterior, deberás crear un `main` para poder ejecutar y mostrar con cual de los siguientes ejemplos funcionan los algoritmos de HC y SA<jupyter_code>#Utiliza estos grdis de prueba
grid1  = '003020600900305001001806400008102900700000008006708200002609500800203009005010300'
grid2  = '4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......'
hard1  = '.....6....59.....82....8....45........3........6..3.54...325..6..................'
def printHCTable(dictionary):
    lineCounter = 0
    blockCounter = 0
    for x in dictionary:
        if(blockCounter == 9):
            print("\n-----")
            blockCounter = 0
        if(lineCounter<=2):
            print(dictionary[x], end='')
            lineCounter += 1
            blockCounter += 1
        else:
            print("\n")
            lineCounter = 0
        
if __name__ == '__main__':
    #hill_climbing(grid1) no es con este porque la grid está como string 
    
    hcEasy =  solve_hill_climbing(grid1)
    hcMedium = solve_hill_climbing(grid2)
    hcHard = solve_hill_climbing(hard1)
    #<Completa>
    print("Hill Climbing Easy")
    print(hcEasy)
    print(' -----------')
    print("Hill Climbing Medium")
    print(hcMedium)
    print(' ------------')
    print("Hill Climbing Hard")
    print(hcHard)
    print(' ------------')
    
    saEasy = solve_simulated_annealing(grid1)
    saMedium = solve_simulated_annealing(grid2)
    saHard = solve_simulated_annealing(hard1)

    #Simulated annealing
    print("Simulated annealing Easy")
    print(saEasy)
    print(' -----------')
    print("Simulated annealing Medium")
    print(saMedium)
    print(' ------------')
    print("Simulated annealing Hard")
    print(saHard)
    print(' ------------')
    print()<jupyter_output>Hill Climbing Easy
{'A1': '4', 'A2': '8', 'A3': '3', 'A4': '9', 'A5': '2', 'A6': '1', 'A7': '6', 'A8': '5', 'A9': '7', 'B1': '9', 'B2': '6', 'B3': '7', 'B4': '3', 'B5': '4', 'B6': '5', 'B7': '8', 'B8': '2', 'B9': '1', 'C1': '2', 'C2': '5', 'C3': '1', 'C4': '8', 'C5': '7', 'C6': '6', 'C7': '4', 'C8': '9', 'C9': '3', 'D1': '5', 'D2': '4', 'D3': '8', 'D4': '1', 'D5': '3', 'D6': '2', 'D7': '9', 'D8': '7', 'D9': '6', 'E1': '7', 'E2': '2', 'E3': '9', 'E4': '5', 'E5': '6', 'E6': '4', 'E7': '1', 'E8': '3', 'E9': '8', 'F1': '1', 'F2': '3', 'F3': '6', 'F4': '7', 'F5': '9', 'F6': '8', 'F7': '2', 'F8': '4', 'F9': '5', 'G1': '3', 'G2': '7', 'G3': '2', 'G4': '6', 'G5': '8', 'G6': '9', 'G7': '5', 'G8': '1', 'G9': '4', 'H1': '8', 'H2': '1', 'H3': '4', 'H4': '2', 'H5': '5', 'H6': '3', 'H7': '7', 'H8': '6', 'H9': '9', 'I1': '6', 'I2': '9', 'I3': '5', 'I4': '4', 'I5': '1', 'I6': '7', 'I7': '3', 'I8': '8', 'I9': '2'}
 -----------
Hill Climbing Medium
{'A1': '4', 'A2': '1', 'A3': '9', 'A4': '3', 'A5': '6',[...]<jupyter_text>Menciona cuál y porqué uno de los ejemplos no tuvo solución en el algoritmo HC Todos los ejemplos anteriores pudieron ser solucionados con HC, sin embargo, en la siguiente secciónvemos que menos de la mitad se pueden resolver con HC. En este caso vemos que para encontrar los sucesores, se swapean 2 valores que no estén fijos en la misma fila, tomando como heuristica la suma de los números de conflictos en cada columna, no se pueden resolver muchos casos.  Posteriormente, deberás ejecutar el archivo de `1000sudoku.txt` y mostrar los resultados obtenidos de ambos algoritmos. Para la comparación, podrías obtener los resultados para 10, 100 y 1000 de ambos algoritmos.<jupyter_code>if __name__ == '__main__':
     #Simulated annealing
    solve_all(from_file("1000sudoku.txt"), "sa", None)
    print()
    
    #Hill Climbing
    solve_all(from_file("1000sudoku.txt"), "hc", None)
    print()<jupyter_output>sa 
 Solved: 978/1000 
 Times (min: 0.00, avg: 0.08, max: 0.60, std dev.: 0.1009) 
 Visited nodes (min: 150, avg: 270.52, max: 414, std dev.: 36.9200)

hc 
 Solved: 421/1000 
 Times (min: 0.01, avg: 0.01, max: 0.06, std dev.: 0.0078) 
 Visited nodes (min: 3, avg: 210.06, max: 334, std dev.: 76.6319)<jupyter_text>Notamos que Simulated annealing logró solucionar la gran  mayoría de los casos, mientras que hill climbing tuvo problemas con más de la mitad para resolver los sudoku ------ Sección 4(Valor: 20)--- Para la implementación del algoritmo genético, deberán completar los códigos a continuación<jupyter_code>import random

def genetic_search(problem, ngen=1000, pmut=0.1, n=20):
    s = problem.initial_state
    states = [problem.result(s, a) for a in problem.actions(s)]
    random.shuffle(states)
    return genetic_algorithm(states[:n], problem.value, ngen, pmut)

def genetic_algorithm(population, fitness_fn, gene_pool=[0, 1], f_thres=None, ngen=1000, pmut=0.1):
    s = problem.initial_state
    states = [problem.result(s, a) for a in problem.actions(s)]
    random.shuffle(states)
    return genetic_algorithm(states[:n], problem.value, ngen, pmut)

def fitness_threshold(fitness_fn, f_thres, population): # el que más letras coincidan de la población
    if not f_thres:
        return None

    fittest_individual = max(population, key=fitness_fn)
    if fitness_fn(fittest_individual) >= f_thres:
        return fittest_individual

    return None

def init_population(pop_number, gene_pool, state_length): 
    g = len(gene_pool)
    population = []
    for i in range(pop_number):
        new_individual = [gene_pool[random.randrange(0, g)] for j in range(state_length)]
        population.append(new_individual)

    return population

def select(r, population, fitness_fn):
    fitnesses = map(fitness_fn, population)
    sampler = weighted_sampler(population, fitnesses)
    return [sampler() for i in range(r)]


def recombine(x, y):
    n = len(x)
    c = random.randrange(0, n)
    return x[:c] + y[c:]


def recombine_uniform(x, y):
    n = len(x)
    result = [0] * n
    indexes = random.sample(range(n), n)
    for i in range(n):
        ix = indexes[i]
        result[ix] = x[ix] if i < n / 2 else y[ix]

    return ''.join(str(r) for r in result)


def mutate(x, gene_pool, pmut):
    if random.uniform(0, 1) >= pmut:
        return x

    n = len(x)
    g = len(gene_pool)
    c = random.randrange(0, n)
    r = random.randrange(0, g)

    new_gene = gene_pool[r]
    return x[:c] + [new_gene] + x[c + 1:]
import bisect
def weighted_sampler(seq, weights):
    totals = []
    for w in weights:
        totals.append(w + totals[-1] if totals else w)
    return lambda: seq[bisect.bisect(totals, random.uniform(0, totals[-1]))]<jupyter_output><empty_output><jupyter_text>¿Cuántas generaciones serán necesarias para obtener la siguiente frase?<jupyter_code>target = 'Quiero mi Diez xD'
u_case = [chr(x) for x in range(65, 91)]
l_case = [chr(x) for x in range(97, 123)]

gene_pool = []
gene_pool.extend(u_case) # adds the uppercase list to the gene pool
gene_pool.extend(l_case) # adds the lowercase list to the gene pool
gene_pool.append(' ')    # adds the space character to the gene pool
max_population = 100 #población para ir encontrando mutaciones correctas
mutation_rate = 0.08# porcentaje de mutación
def fitness_fn(sample):
    fitness = 0
    for i in range(len(sample)):
        if sample[i] == target[i]:
                fitness += 1
    return fitness 
ngen = 2000 #Limite max
f_thres =len(target) # la longitud del string que queremos generar
def genetic_algorithm_stepwise(population, fitness_fn, gene_pool=[0, 1], f_thres=None, ngen=1200, pmut=0.1):
    for generation in range(ngen):
        population = [mutate(recombine(*select(2, population, fitness_fn)), gene_pool, pmut) for i in range(len(population))]
        current_best = ''.join(max(population, key=fitness_fn))
        print(f'Current best: {current_best}\t\tGeneration: {str(generation)}\t\tFitness: {fitness_fn(current_best)}\r', end='')

        fittest_individual = fitness_threshold(fitness_fn, f_thres, population)

        if fittest_individual:
            return fittest_individual, generation
    return max(population, key=fitness_fn) , generation       
population = init_population(max_population, gene_pool, len(target))
parents = select(2, population, fitness_fn) 
child = recombine(*parents)
child = mutate(child, gene_pool, mutation_rate)
population = [mutate(recombine(*select(2, population, fitness_fn)), gene_pool, mutation_rate) for i in range(len(population))]
current_best = max(population, key=fitness_fn)
current_best_string = ''.join(current_best)

population = init_population(max_population, gene_pool, len(target))
solution, generations = genetic_algorithm_stepwise(population, fitness_fn, gene_pool, f_thres, ngen, mutation_rate)<jupyter_output>Current best: Quiero mi Diez xD		Generation: 1176		Fitness: 17<jupyter_text>Tras correr múltiples veces el algoritmo, determinamos que un buen límite de generaciones es 2000 para usualmente obtener que la frase sea encontrada, sin embargo, es altamente posible que se encuentre desde la gen 1000, dependiendo cómo vayan surgiendo las mutaciones, o incluso que sobrepase por mucho 2000 generaciones ---------------- Ejemplo EXTRA Pequeño ejercicio detección de máscaras faciales. (Valor: 15 puntos extras) En la siguiente sección cargamos los paths para las imagenes que se usarán en el modelo de entrenamiento, su objetivo será ejecutar satisfactoriamente el siguiente código y mostrar ejemplos de su funcionamiento.En la carpeta de ejemplos vienen unas pruebas de imágenes de gente con y sin mascara. Deberán cargar otros ejemplos y ver si el algoritmo falla. En la última sección, deberán mostrar que pudieron habilitar el código para ejecutarlo con la cámara de su computadora. ---En este apartado se codifican las imágenes para crear nuestro modelo de detección de rostros con y sin máscara.Posiblemente se tarde 20-40 minutos si ejecutan este código en sus computadoras, a menos que trabajen con la GPU... Entrenen su modelo y traten de analizar lo que está sucediendo...<jupyter_code>from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.layers import AveragePooling2D
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing.image import load_img
from tensorflow.keras.utils import to_categorical
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import os

# initialize the initial learning rate, number of epochs to train for,
# and batch size
INIT_LR = 1e-4
EPOCHS = 20
BS = 32

# grab the list of images in our dataset directory, then initialize
# the list of data (i.e., images) and class images
print("[INFO] loading images...")
imagePaths = list(paths.list_images("dataset"))
data = []
labels = []

# loop over the image paths
for imagePath in imagePaths:
	# extract the class label from the filename
	label = imagePath.split(os.path.sep)[-2]

	# load the input image (224x224) and preprocess it
	image = load_img(imagePath, target_size=(224, 224))
	image = img_to_array(image)
	image = preprocess_input(image)

	# update the data and labels lists, respectively
	data.append(image)
	labels.append(label)

# convert the data and labels to NumPy arrays
data = np.array(data, dtype="float32")
labels = np.array(labels)

# perform one-hot encoding on the labels
lb = LabelBinarizer()
labels = lb.fit_transform(labels)
labels = to_categorical(labels)

# partition the data into training and testing splits using 75% of
# the data for training and the remaining 25% for testing
(trainX, testX, trainY, testY) = train_test_split(data, labels,
	test_size=0.20, stratify=labels, random_state=42)

# construct the training image generator for data augmentation
aug = ImageDataGenerator(
	rotation_range=20,
	zoom_range=0.15,
	width_shift_range=0.2,
	height_shift_range=0.2,
	shear_range=0.15,
	horizontal_flip=True,
	fill_mode="nearest")

# load the MobileNetV2 network, ensuring the head FC layer sets are
# left off
baseModel = MobileNetV2(weights="imagenet", include_top=False,
	input_tensor=Input(shape=(224, 224, 3)))

# construct the head of the model that will be placed on top of the
# the base model
headModel = baseModel.output
headModel = AveragePooling2D(pool_size=(7, 7))(headModel)
headModel = Flatten(name="flatten")(headModel)
headModel = Dense(128, activation="relu")(headModel)
headModel = Dropout(0.5)(headModel)
headModel = Dense(2, activation="softmax")(headModel)

# place the head FC model on top of the base model (this will become
# the actual model we will train)
model = Model(inputs=baseModel.input, outputs=headModel)

# loop over all layers in the base model and freeze them so they will
# *not* be updated during the first training process
for layer in baseModel.layers:
	layer.trainable = False

# compile our model
print("[INFO] compiling model...")
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="binary_crossentropy", optimizer=opt,
	metrics=["accuracy"])

# train the head of the network
print("[INFO] training head...")
H = model.fit(
	aug.flow(trainX, trainY, batch_size=BS),
	steps_per_epoch=len(trainX) // BS,
	validation_data=(testX, testY),
	validation_steps=len(testX) // BS,
	epochs=EPOCHS)

# make predictions on the testing set
print("[INFO] evaluating network...")
predIdxs = model.predict(testX, batch_size=BS)

# for each image in the testing set we need to find the index of the
# label with corresponding largest predicted probability
predIdxs = np.argmax(predIdxs, axis=1)

# show a nicely formatted classification report
print(classification_report(testY.argmax(axis=1), predIdxs,
	target_names=lb.classes_))

# serialize the model to disk
print("[INFO] saving mask detector model...")
model.save("maskDetectorTest.model", save_format="h5")

# plot the training loss and accuracy
N = EPOCHS
plt.style.use("ggplot")
plt.figure()
plt.plot(np.arange(0, N), H.history["loss"], label="train_loss")
plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss")
plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc")
plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig("plot.png")<jupyter_output><empty_output><jupyter_text>En esta sección, deberán mostrar ejemplos satisfactorios y fallidos del modelo de máscaras<jupyter_code># import the necessary packages
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
import numpy as np
import argparse
import cv2
import os

print("[INFO] loading face detector model...")
net = cv2.dnn.readNet("face_detector/deploy.prototxt", "face_detector/res10_300x300_ssd_iter_140000.caffemodel")

# load the face mask detector model from disk
print("[INFO] loading face mask detector model...")
model = load_model('maskDetectorTest.model') # El que se acaba de escribir

# load the input image from disk, clone it, and grab the image spatial
# dimensions
# Falla al detectar la mascara en example_03.png
image = cv2.imread('examples/example_03.png')
orig = image.copy()
(h, w) = image.shape[:2]

# construct a blob from the image
blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300),
	(104.0, 177.0, 123.0))

# pass the blob through the network and obtain the face detections
print("[INFO] computing face detections...")
net.setInput(blob)
detections = net.forward()

# loop over the detections
for i in range(0, detections.shape[2]):
	# extract the confidence (i.e., probability) associated with
	# the detection
	confidence = detections[0, 0, i, 2]

	# filter out weak detections by ensuring the confidence is
	# greater than the minimum confidence
	if confidence > 0.5:
		# compute the (x, y)-coordinates of the bounding box for
		# the object
		box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
		(startX, startY, endX, endY) = box.astype("int")

		# ensure the bounding boxes fall within the dimensions of
		# the frame
		(startX, startY) = (max(0, startX), max(0, startY))
		(endX, endY) = (min(w - 1, endX), min(h - 1, endY))

		# extract the face ROI, convert it from BGR to RGB channel
		# ordering, resize it to 224x224, and preprocess it
		face = image[startY:endY, startX:endX]
		face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
		face = cv2.resize(face, (224, 224))
		face = img_to_array(face)
		face = preprocess_input(face)
		face = np.expand_dims(face, axis=0)

		# pass the face through the model to determine if the face
		# has a mask or not
		(mask, withoutMask) = model.predict(face)[0]

		# determine the class label and color we'll use to draw
		# the bounding box and text
		label = "Mask" if mask > withoutMask else "No Mask"
		color = (0, 255, 0) if label == "Mask" else (0, 0, 255)

		# include the probability in the label
		label = "{}: {:.2f}%".format(label, max(mask, withoutMask) * 100)

		# display the label and bounding box rectangle on the output
		# frame
		cv2.putText(image, label, (startX, startY - 10),
			cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
		cv2.rectangle(image, (startX, startY), (endX, endY), color, 2)

# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)<jupyter_output>[INFO] loading face detector model...
[INFO] loading face mask detector model...
[INFO] computing face detections...<jupyter_text>Completa algunas líneas para que funcione con la cámara...  Video del reconocimiento de cubrebocas [Demo](https://drive.google.com/file/d/1W6dfW0Rb9-LXhxrvAsUoc4sehUNRdEKf/view).<jupyter_code># import the necessary packages
from tensorflow.keras.applications.mobilenet_v2 import preprocess_input
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.models import load_model
from imutils.video import VideoStream
import numpy as np
import argparse
import imutils
import time
import cv2
import os

def detect_and_predict_mask(frame, faceNet, maskNet):
	# grab the dimensions of the frame and then construct a blob
	# from it
	(h, w) = frame.shape[:2]
	blob = cv2.dnn.blobFromImage(frame, 1.0, (300, 300),(104.0, 177.0, 123.0))

	# pass the blob through the network and obtain the face detections
	faceNet.setInput(blob)
	detections = faceNet.forward()

	# initialize our list of faces, their corresponding locations,
	# and the list of predictions from our face mask network
	faces = []
	locs = []
	preds = []

	# loop over the detections
	for i in range(0, detections.shape[2]):
		# extract the confidence (i.e., probability) associated with
		# the detection
		confidence = detections[0, 0, i, 2]

		# filter out weak detections by ensuring the confidence is
		# greater than the minimum confidence
		if confidence > 0.5:
			# compute the (x, y)-coordinates of the bounding box for
			# the object
			box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
			(startX, startY, endX, endY) = box.astype("int")

			# ensure the bounding boxes fall within the dimensions of
			# the frame
			(startX, startY) = (max(0, startX), max(0, startY))
			(endX, endY) = (min(w - 1, endX), min(h - 1, endY))

			# extract the face ROI, convert it from BGR to RGB channel
			# ordering, resize it to 224x224, and preprocess it
			face = frame[startY:endY, startX:endX]
			face = cv2.cvtColor(face, cv2.COLOR_BGR2RGB)
			face = cv2.resize(face, (224, 224))
			face = img_to_array(face)
			face = preprocess_input(face)
			face = np.expand_dims(face, axis=0)

			# add the face and bounding boxes to their respective
			# lists
			faces.append(face)
			locs.append((startX, startY, endX, endY))

	# only make a predictions if at least one face was detected
	if len(faces) > 0:
		# for faster inference we'll make batch predictions on *all*
		# faces at the same time rather than one-by-one predictions
		# in the above `for` loop
		preds = maskNet.predict(faces)

	# return a 2-tuple of the face locations and their corresponding
	# locations
	return (locs, preds)

# load our serialized face detector model from disk
print("[INFO] loading face detector model...")
faceNet = cv2.dnn.readNet("face_detector/deploy.prototxt", "face_detector/res10_300x300_ssd_iter_140000.caffemodel")

# load the face mask detector model from disk
print("[INFO] loading face mask detector model...")
maskNet = load_model('maskDetectorTest.model') # El que se acaba de escribir

# initialize the video stream and allow the camera sensor to warm up
print("[INFO] starting video stream...")
vs = VideoStream(src=0).start() # cambiar dependiendo si es webcam de laptop o externa o de otro programa
time.sleep(2.0)

# loop over the frames from the video stream
while True:
	# grab the frame from the threaded video stream and resize it
	# to have a maximum width of 400 pixels
	frame = vs.read()
	frame = imutils.resize(frame, width=400)

	# detect faces in the frame and determine if they are wearing a
	# face mask or not
	(locs, preds) = detect_and_predict_mask(frame, faceNet, maskNet)

	# loop over the detected face locations and their corresponding
	# locations
	for (box, pred) in zip(locs, preds):
		# unpack the bounding box and predictions
		(startX, startY, endX, endY) = box
		(mask, withoutMask) = pred

		# determine the class label and color we'll use to draw
		# the bounding box and text
		label = "Mask" if mask > withoutMask else "No Mask"
		color = (0, 255, 0) if label == "Mask" else (0, 0, 255)

		# include the probability in the label
		label = "{}: {:.2f}%".format(label, max(mask, withoutMask) * 100)

		# display the label and bounding box rectangle on the output
		# frame
		cv2.putText(frame, label, (startX, startY - 10),
			cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
		cv2.rectangle(frame, (startX, startY), (endX, endY), color, 2)

	# show the output frame
	cv2.imshow("Frame", frame)
	key = cv2.waitKey(1) & 0xFF

	# if the `q` key was pressed, break from the loop
	if key == ord("q"):
		break

# do a bit of cleanup
cv2.destroyAllWindows()
vs.stop()<jupyter_output>[INFO] loading face detector model...
[INFO] loading face mask detector model...
[INFO] starting video stream...
{'fraction_non_alphanumeric': 0.1246705148310033, 'fraction_numerical': 0.03675219378732775, 'mean_word_length': 3.1331448665793284, 'pattern_counts': {'":': 0, '<': 62, '<?xml version=': 0, '>': 119, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '15', 'dir': 'jupyter-structured-clean-dedup', 'id': '454794', 'license': 'MIT', 'n_tokens_mistral': 21745, 'n_tokens_neox': 20014, 'n_words': 7170, 'path': 'Examen_Parcial_1/Examen Parcial 1 Sistemas Inteligentes.ipynb', 'repo_name': 'JulianHerreraH/Sistemas_Inteligentes', 'size': '59971'}