用Python+PyTorch实战AI导论:从理论到代码的沉浸式学习

人工智能导论课程往往充斥着抽象概念和数学公式,让许多初学者望而生畏。但真正的理解来自于实践——当你亲手用代码实现那些课本上的算法时,理论会突然变得清晰起来。本文将带你用Python和PyTorch搭建一个"AI实验室",把枯燥的课本知识转化为可运行的代码。

1. 搭建AI算法实验环境

在开始编码之前,我们需要配置一个适合AI算法实验的开发环境。不同于普通的Python开发,AI实验对库版本和硬件有一定要求。

基础环境配置:

conda create -n ai_lab python=3.8
conda activate ai_lab
pip install torch==1.9.0 torchvision==0.10.0

核心工具包选择:

  • PyTorch :相比TensorFlow更Pythonic,适合教学和实验
  • NumPy :处理矩阵运算和数值计算
  • Matplotlib :可视化算法过程和结果
  • Jupyter Notebook :交互式实验的理想选择

提示:如果使用GPU加速,需要安装对应版本的CUDA驱动。但大多数基础算法在CPU上也能良好运行。

下面是一个环境检查脚本,可以验证主要组件是否正常工作:

import torch
print(f"PyTorch版本: {torch.__version__}")
print(f"CUDA可用: {torch.cuda.is_available()}")

2. 谓词逻辑的编程实现

一阶谓词逻辑是AI知识表示的基础,我们可以用Python类来模拟这一系统。

谓词逻辑系统设计:

class Predicate:
    def __init__(self, name, arity):
        self.name = name  # 谓词名称
        self.arity = arity  # 参数数量
        
    def __call__(self, *args):
        if len(args) != self.arity:
            raise ValueError(f"谓词{self.name}需要{self.arity}个参数")
        return Atom(self, args)

class Atom:
    def __init__(self, predicate, args):
        self.predicate = predicate
        self.args = args
        
    def __repr__(self):
        args_str = ",".join(str(arg) for arg in self.args)
        return f"{self.predicate.name}({args_str})"

实际应用示例:

# 定义谓词
Teacher = Predicate("Teacher", 1)
Father = Predicate("Father", 2)
Likes = Predicate("Likes", 2)

# 构建原子公式
statement1 = Teacher("MrWang")
statement2 = Father("MrWang", "XiaoMing")
statement3 = Likes("XiaoHong", "Music")

print(statement1)  # 输出: Teacher(MrWang)
print(statement2)  # 输出: Father(MrWang,XiaoMing)

归结原理的实现:

def resolve(clause1, clause2):
    """简单的归结推理实现"""
    substitutions = unify(clause1, clause2)
    if substitutions:
        new_clause = apply_substitutions(clause1 + clause2, substitutions)
        return simplify(new_clause)
    return None

3. 搜索算法的Python实现

搜索是AI解决问题的基本策略,让我们实现几种经典搜索算法。

3.1 A*搜索算法

def a_star_search(start, goal, heuristic):
    open_set = PriorityQueue()
    open_set.put(start, 0)
    came_from = {}
    g_score = {start: 0}
    
    while not open_set.empty():
        current = open_set.get()
        
        if current == goal:
            return reconstruct_path(came_from, current)
            
        for neighbor in get_neighbors(current):
            tentative_g = g_score[current] + distance(current, neighbor)
            
            if neighbor not in g_score or tentative_g < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = tentative_g
                f_score = tentative_g + heuristic(neighbor, goal)
                open_set.put(neighbor, f_score)
    
    return None  # 未找到路径

3.2 八数码问题实战

class EightPuzzle:
    def __init__(self, initial_state):
        self.state = initial_state
        self.goal = [[1,2,3],[4,5,6],[7,8,0]]
        
    def heuristic(self, state):
        """曼哈顿距离启发式"""
        distance = 0
        for i in range(3):
            for j in range(3):
                if state[i][j] != 0:
                    x, y = divmod(state[i][j]-1, 3)
                    distance += abs(x - i) + abs(y - j)
        return distance
    
    def get_successors(self):
        """生成所有可能的下一步状态"""
        # 找到空格位置
        for i in range(3):
            for j in range(3):
                if self.state[i][j] == 0:
                    x, y = i, j
                    break
                    
        successors = []
        for dx, dy in [(0,1),(1,0),(0,-1),(-1,0)]:
            nx, ny = x + dx, y + dy
            if 0 <= nx < 3 and 0 <= ny < 3:
                new_state = [row[:] for row in self.state]
                new_state[x][y], new_state[nx][ny] = new_state[nx][ny], new_state[x][y]
                successors.append(new_state)
                
        return successors

4. 遗传算法框架实现

遗传算法模拟自然选择过程,是解决优化问题的强大工具。

基本遗传算法框架:

import random
import numpy as np

class GeneticAlgorithm:
    def __init__(self, population_size, chromosome_length, fitness_func):
        self.pop_size = population_size
        self.chrom_len = chromosome_length
        self.fitness_func = fitness_func
        self.population = self.init_population()
        
    def init_population(self):
        return np.random.randint(2, size=(self.pop_size, self.chrom_len))
    
    def selection(self, fitness):
        """轮盘赌选择"""
        probs = fitness / fitness.sum()
        return np.random.choice(
            range(self.pop_size), 
            size=self.pop_size, 
            p=probs
        )
    
    def crossover(self, parent1, parent2, rate=0.8):
        if random.random() < rate:
            pt = random.randint(1, self.chrom_len-2)
            child1 = np.concatenate([parent1[:pt], parent2[pt:]])
            child2 = np.concatenate([parent2[:pt], parent1[pt:]])
            return child1, child2
        return parent1.copy(), parent2.copy()
    
    def mutation(self, chromosome, rate=0.01):
        for i in range(self.chrom_len):
            if random.random() < rate:
                chromosome[i] = 1 - chromosome[i]
        return chromosome
    
    def evolve(self, generations):
        best_fitness = []
        for gen in range(generations):
            fitness = np.array([self.fitness_func(ind) for ind in self.population])
            best_fitness.append(fitness.max())
            
            # 选择
            selected = self.selection(fitness)
            
            # 交叉和变异
            new_pop = []
            for i in range(0, self.pop_size, 2):
                p1, p2 = selected[i], selected[i+1]
                c1, c2 = self.crossover(self.population[p1], self.population[p2])
                new_pop.extend([self.mutation(c1), self.mutation(c2)])
            
            self.population = np.array(new_pop)
        
        return best_fitness

应用示例:求解函数最大值

def test_fitness(chromosome):
    """适应度函数示例:求二进制编码表示的数字的平方"""
    num = int("".join(map(str, chromosome)), 2)
    return num ** 2

ga = GeneticAlgorithm(
    population_size=50,
    chromosome_length=10,
    fitness_func=test_fitness
)

fitness_history = ga.evolve(100)

5. 群智能算法实践

群智能算法模拟昆虫或鸟群等生物群体的集体行为,具有自组织和分布式特点。

5.1 蚁群算法实现

class AntColony:
    def __init__(self, distances, n_ants, n_best, n_iterations, decay, alpha=1, beta=1):
        self.distances = distances
        self.pheromone = np.ones(self.distances.shape) / len(distances)
        self.all_inds = range(len(distances))
        self.n_ants = n_ants
        self.n_best = n_best
        self.n_iterations = n_iterations
        self.decay = decay
        self.alpha = alpha
        self.beta = beta
        
    def run(self):
        shortest_path = None
        best_length = float('inf')
        
        for it in range(self.n_iterations):
            all_paths = self.gen_all_paths()
            self.spread_pheromone(all_paths)
            
            current_shortest = min(all_paths, key=lambda x: x[1])
            if current_shortest[1] < best_length:
                best_length = current_shortest[1]
                shortest_path = current_shortest[0]
                
            self.pheromone *= self.decay
            
        return shortest_path, best_length
    
    def gen_path_dist(self, path):
        return sum(self.distances[path[i], path[i+1]] for i in range(len(path)-1))
    
    def gen_all_paths(self):
        all_paths = []
        for _ in range(self.n_ants):
            path = self.gen_path()
            all_paths.append((path, self.gen_path_dist(path)))
        return all_paths
    
    def gen_path(self):
        path = []
        visited = set()
        visited.add(0)
        prev = 0
        for _ in range(len(self.distances)-1):
            move = self.pick_move(self.pheromone[prev], self.distances[prev], visited)
            path.append((prev, move))
            prev = move
            visited.add(move)
        path.append((prev, 0))  # 回到起点
        return [i for i,j in path]
    
    def pick_move(self, pheromone, dist, visited):
        pheromone = np.copy(pheromone)
        pheromone[list(visited)] = 0
        
        row = pheromone ** self.alpha * ((1.0 / (dist + 1e-10)) ** self.beta)
        norm_row = row / row.sum()
        
        move = np.random.choice(self.all_inds, 1, p=norm_row)[0]
        return move
    
    def spread_pheromone(self, all_paths):
        sorted_paths = sorted(all_paths, key=lambda x: x[1])
        for path, dist in sorted_paths[:self.n_best]:
            for i,j in zip(path[:-1], path[1:]):
                self.pheromone[i,j] += 1.0 / dist

5.2 粒子群优化算法

class ParticleSwarmOptimizer:
    def __init__(self, n_particles, dimensions, objective_func, 
                 w=0.7, c1=1.5, c2=1.5):
        self.n_particles = n_particles
        self.dimensions = dimensions
        self.objective = objective_func
        self.w = w  # 惯性权重
        self.c1 = c1  # 认知系数
        self.c2 = c2  # 社会系数
        
        # 初始化粒子位置和速度
        self.positions = np.random.uniform(-5, 5, (n_particles, dimensions))
        self.velocities = np.random.uniform(-1, 1, (n_particles, dimensions))
        
        # 记录个体最优和全局最优
        self.pbest_pos = self.positions.copy()
        self.pbest_val = np.array([float('inf')] * n_particles)
        self.gbest_pos = None
        self.gbest_val = float('inf')
        
    def optimize(self, max_iter):
        history = []
        for _ in range(max_iter):
            # 评估当前粒子群
            current_val = np.array([self.objective(p) for p in self.positions])
            
            # 更新个体最优
            improved_idx = current_val < self.pbest_val
            self.pbest_pos[improved_idx] = self.positions[improved_idx]
            self.pbest_val[improved_idx] = current_val[improved_idx]
            
            # 更新全局最优
            if current_val.min() < self.gbest_val:
                self.gbest_val = current_val.min()
                self.gbest_pos = self.positions[current_val.argmin()].copy()
            
            # 更新速度和位置
            r1 = np.random.random((self.n_particles, self.dimensions))
            r2 = np.random.random((self.n_particles, self.dimensions))
            
            cognitive = self.c1 * r1 * (self.pbest_pos - self.positions)
            social = self.c2 * r2 * (self.gbest_pos - self.positions)
            
            self.velocities = self.w * self.velocities + cognitive + social
            self.positions += self.velocities
            
            history.append(self.gbest_val)
        
        return self.gbest_pos, self.gbest_val, history

在实际项目中,我发现将理论算法转化为代码时,最重要的是保持算法的核心思想不变,同时根据编程语言的特性进行适当调整。比如遗传算法中的选择操作,理论上应该严格按概率选择,但在实际编码时,可以加入精英保留策略来加速收敛。

更多推荐