返回学习地图
pythonPhase A · 第 2

01. Python速成

从零到NumPy:Python语法、列表、字典、函数、类、NumPy向量化计算

19 个章节·按类别展开/折叠
知识

为什么学Python?——AI世界的通用语言

Python是AI领域的通用语言。就像一个国际机场,不管你去哪里,都得先经过它:

  • PyTorch、TensorFlow、JAX——三大深度学习框架都用Python
  • NumPy、Pandas、Scikit-learn——数据处理全栈Python
  • HuggingFace Transformers——主流LLM接口都是Python
  • LangChain、AutoGen、CrewAI——Agent框架全用Python

为什么Python赢了?

  • 语法极简:print('Hello')——一行代码。Java要写一堆public static void main。
  • 库丰富:pip install anything。几乎任何AI工具都有Python包。
  • 社区巨大:StackOverflow、GitHub上Python相关问答和项目最多。
  • 胶水语言:底层C/C++加速(NumPy、PyTorch),Python层只是调用接口。你不需要管底层。

好消息是:学AI只需要Python的基础子集——变量、列表、字典、函数、类、NumPy。不用学装饰器、元类、协程这些高级特性。

知识

变量与数据类型——Python的基本积木

变量就是"贴标签"——把一个名字贴到一个值上。

类比:就像把一件礼物贴上名字标签。你用标签找到礼物,计算机用变量名找到数据。

基础数据类型:

  • 整数(int):age = 25。没有小数点的数字。
  • 浮点数(float):pi = 3.14159。带小数点的数字。
  • 字符串(str):name = 'Alice'。文本,用引号包着。
  • 布尔值(bool):is_student = True。只有True和False两个值。
  • None:result = None。表示"没有值",像是一个空盒子。

Python的方便之处:

  • 不用声明类型:直接写 name = 'Alice',Python自动知道是字符串
  • 类型可变:x = 5(int),然后 x = 'hello'(str)——合法的(但不推荐)
  • type()函数查看类型:type(3.14) → float

字符串操作(AI中最常用):

  • 拼接:'Hello' + ' ' + 'World' → 'Hello World'
  • 格式化:f'Name: {name}, Age: {age}' → 'Name: Alice, Age: 25'
  • 切片:text = 'Python'; text[0:3] → 'Pyt'(前3个字符)
  • 长度:len('Python') → 6
代码

变量与数据类型——动手试试

# ===== 变量赋值 =====
name = 'Alice'       # 字符串
age = 25             # 整数
height = 1.75        # 浮点数
is_student = True    # 布尔值

# 类型自动推断
print(type(name))    # <class 'str'>
print(type(age))     # <class 'int'>
print(type(height))  # <class 'float'>

# 字符串格式化(AI中最常用!)
print(f'{name} is {age} years old')
# 输出: Alice is 25 years old

# 类型转换
age_str = str(age)   # 25 -> '25'
age_num = int('25')  # '25' -> 25

# 常见的坑:输入默认是字符串
text_input = '3.14'
# float(text_input) -> 3.14  # 正确
# int(text_input)   -> 报错  # 不能直接转整数
# int(float(text_input)) -> 3  # 先转float再int
知识

列表(List)——有序数据的容器

列表是最常用的数据结构,可以放任意类型的数据。

📦 购物车类比:你把要买的东西按顺序放进购物车——['苹果', '牛奶', '面包', '鸡蛋']。你可以加东西、取出一部分、换顺序。

列表的核心操作(必须掌握):

创建:scores = [85, 90, 78, 92]

读取:#1 按索引(从0开始):

  • scores[0] → 85(第一个元素)
  • scores[-1] → 92(最后一个元素)
  • scores[1:3] → [90, 78](第2到第3个,不含第4个)

修改:

  • scores.append(88) → [85, 90, 78, 92, 88](末尾追加)
  • scores.insert(0, 95) → [95, 85, 90, 78, 92, 88](开头插入)
  • scores.pop() → 88,列表变为[95, 85, 90, 78, 92](弹出最后一个)
  • scores.remove(90) → [95, 85, 78, 92](删除第一个匹配的值)

遍历:#1 普通遍历:for s in scores: print(s)

#2 带索引遍历:for i, s in enumerate(scores): print(f'{i}: {s}')

#3 列表推导式(Python特色):squares = [x**2 for x in range(10)] → [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

排序:

  • scores.sort() → 原地升序排序
  • sorted(scores) → 返回新排序列表,原列表不变
  • scores.sort(reverse=True) → 降序
代码

列表操作实战

# ===== 列表创建 =====
scores = [85, 90, 78, 92]
print(len(scores))  # 4

# ===== 访问 =====
print(scores[0])   # 85(第1个)
print(scores[-1])  # 92(最后1个)
print(scores[1:3]) # [90, 78](索引1到2)

# ===== 修改 =====
scores.append(88)   # [85, 90, 78, 92, 88]
scores.insert(0, 95)  # [95, 85, 90, 78, 92, 88]
last = scores.pop()   # 88
print(scores)       # [95, 85, 90, 78, 92]

# ===== 遍历 =====
for s in scores:
    print(s * 1.1)  # 加10%后打印

for i, s in enumerate(scores):
    if s >= 90:
        print(f'{i}: 优秀({s})')

# ===== 列表推导式 =====
squares = [x**2 for x in range(10)]
# 相当于:
# squares = []
# for x in range(10):
#     squares.append(x**2)
print(squares)  # [0,1,4,9,16,25,36,49,64,81]

# ===== 高级用法:条件推导 =====
# 筛选出>=90的分数
good = [s for s in scores if s >= 90]
print(good)  # [95, 92]
知识

字典(Dict)——键值对数据容器

字典存储键值对——每个"键"对应一个"值"。

📖 字典类比:真正的词典。你查单词(键)得到定义(值)。不能查定义反向得到单词。

为什么AI中频繁用字典?

  • 超参数配置:config = {'lr': 0.001, 'batch_size': 32, 'epochs': 100}
  • 样本标签对:dataset = {'img1.png': 0, 'img2.png': 1}
  • 统计数据:stats = {'accuracy': 0.92, 'loss': 0.35}

字典的核心操作:

创建:student = {'name': 'Alice', 'age': 25, 'scores': [85, 90]}

读取:

  • student['name'] → 'Alice'
  • student.get('grade', 'N/A') → 'N/A'(安全读取,不存在返回默认值)

修改:

  • student['age'] = 26(修改已有键)
  • student['city'] = 'Beijing'(新增键值对)

遍历:

  • for key in student: print(key, student[key])(遍历键)
  • for key, val in student.items(): print(key, val)(遍历键值对)
代码

字典操作实战

# ===== 创建字典 =====
student = {
    'name': 'Alice',
    'age': 25,
    'scores': [85, 90, 78, 92],
    'is_active': True
}

# ===== 读取 =====
print(student['name'])           # 'Alice'
print(student.get('grade'))      # None(安全访问)
print(student.get('grade', 'N/A'))  # 'N/A'(默认值)

# ===== 修改/新增 =====
student['age'] = 26             # 修改
student['city'] = 'Beijing'     # 新增

# ===== 遍历 =====
for key, value in student.items():
    print(f'{key}: {value}')

# ===== 字典推导式 =====
squares_dict = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# ===== 嵌套字典 =====
config = {
    'model': {
        'hidden_size': 768,
        'num_layers': 12
    },
    'training': {
        'lr': 0.001,
        'batch_size': 32
    }
}
print(config['model']['hidden_size'])  # 768
知识

条件与循环——控制程序的流程

条件语句(if/elif/else):让程序根据情况做不同的事。

🚦 红绿灯类比:红灯停,绿灯行,黄灯等一等——就是if-elif-else。

基本结构:

if 条件1:
    做事情A
elif 条件2:
    做事情B
else:
    做事情C

比较运算符:==(等于)、!=(不等于)、>、<、>=、<=

逻辑运算符:and(且)、or(或)、not(非)

循环(for/while):重复做某件事。

  • for循环:遍历一个序列(如列表、字符串、range)。确定次数。for x in range(5): print(x) → 0,1,2,3,4
  • while循环:条件为真就一直做。不确定次数。while score < 100: score += 1

range(start, stop, step):生成整数序列。

  • range(5) → 0,1,2,3,4(不含5)
  • range(2, 7) → 2,3,4,5,6
  • range(0, 10, 2) → 0,2,4,6,8

break和continue:

  • break:跳出整个循环
  • continue:跳过当前迭代,继续下一次
代码

条件与循环实战

# ===== if-elif-else =====
score = 85
if score >= 90:
    grade = 'A'
elif score >= 80:
    grade = 'B'
elif score >= 70:
    grade = 'C'
else:
    grade = 'D'
print(f'Score {score}: {grade}')  # Score 85: B

# ===== 逻辑组合 =====
temp = 35
humidity = 80
if temp > 30 and humidity > 70:
    print('又热又闷')  # 会输出

# ===== for循环 =====
for i in range(5):
    print(i, end=' ')  # 0 1 2 3 4
    
print()  # 换行

# 遍历列表
fruits = ['apple', 'banana', 'cherry']
for fruit in fruits:
    print(f'I like {fruit}')

# ===== 带索引遍历 =====
for i, fruit in enumerate(fruits):
    print(f'{i}: {fruit}')

# ===== while循环 =====
count = 0
while count < 3:
    print(f'Count: {count}')
    count += 1

# ===== break示例 =====
for num in range(10):
    if num == 5:
        break  # 遇到5就跳出
    print(num, end=' ')  # 0 1 2 3 4
知识

函数——封装可复用的代码块

函数是一段可以反复调用的代码。就像一台咖啡机——你按一下按钮(调用函数),它执行一系列步骤(磨豆、加热、冲泡),给你输出(咖啡)。

为什么用函数?

  • 复用:写一次,用很多次
  • 可读:把复杂逻辑拆成有名字的小块
  • 测试:每个函数可以单独测试

函数定义语法:

def 函数名(参数1, 参数2=默认值):
    """文档字符串——说明函数做什么"""
    执行代码
    return 返回值

关键概念:

  • 参数:输入给函数的值
  • 返回值:函数输出的结果。没有return返回None
  • 默认参数:def greet(name, greeting='Hello'),不传greeting就用'Hello'
  • Docstring:函数的说明文档,用三重引号
代码

函数实战

# ===== 简单函数 =====
def greet(name, greeting='Hello'):
    """用指定问候语向某人打招呼"""
    return f'{greeting}, {name}!'

print(greet('Alice'))          # Hello, Alice!
print(greet('Bob', 'Hi'))      # Hi, Bob!

# ===== 计算函数 =====
def sigmoid(x):
    """Sigmoid激活函数"""
    return 1 / (1 + 2.71828**-x)

print(sigmoid(0))   # 0.5
print(sigmoid(2))   # ~0.8808

# ===== 多返回值 =====
def compute_stats(numbers):
    """计算列表的均值和标准差(简化版)"""
    n = len(numbers)
    mean = sum(numbers) / n
    variance = sum((x - mean)**2 for x in numbers) / n
    std = variance**0.5
    return mean, std

avg, std = compute_stats([85, 90, 78, 92])
print(f'平均分: {avg:.1f}, 标准差: {std:.1f}')
# 平均分: 86.2, 标准差: 5.5

# ===== 函数作为参数 =====
def apply_to_all(func, data):
    """对data中每个元素应用func"""
    return [func(x) for x in data]

scores = [1, 2, 3, 4, 5]
doubled = apply_to_all(lambda x: x*2, scores)
print(doubled)  # [2, 4, 6, 8, 10]
知识

类(Class)——数据和行为的封装

就像一张蓝图。你根据蓝图造出一个个"对象"(实例)。

🏭 类比:

  • 类 = 汽车的设计图纸——定义了所有汽车的结构
  • 对象 = 根据图纸造出的一辆具体汽车——有自己的颜色、车牌号

为什么AI中要用类?

  • 模型定义:nn.Module是PyTorch中所有模型的基类
  • 数据集:torch.utils.data.Dataset
  • 层:nn.Linear, nn.Conv2d等都是类

类的核心元素:

  • __init__:创建对象时自动调用,初始化属性
  • self:代表对象本身。方法第一个参数永远是self
  • 属性:对象的数据(如self.name, self.weights)
  • 方法:对象的函数(如forward())
代码

类实战——实现一个简单神经网络层

# ===== 定义一个简单的线性层 =====
class LinearLayer:
    """模拟神经网络中的一个全连接层"""
    
    def __init__(self, input_size, output_size):
        """
        初始化权重和偏置
        input_size: 输入特征数
        output_size: 输出特征数
        """
        # 1) 用随机小数值初始化权重
        self.weights = [[0.01] * output_size for _ in range(input_size)]
        # 2) 偏置初始化为0
        self.bias = [0.0] * output_size
        self.input_size = input_size
        self.output_size = output_size
        
    def forward(self, x):
        """
        前向传播:y = Wx + b
        x: 输入向量,长度为input_size
        返回:输出向量,长度为output_size
        """
        y = []
        for j in range(self.output_size):
            # 计算第j个输出的值
            val = self.bias[j]
            for i in range(self.input_size):
                val += x[i] * self.weights[i][j]
            y.append(val)
        return y
    
    def __repr__(self):
        return f'LinearLayer({self.input_size} -> {self.output_size})'


# ===== 使用这个类 =====
layer = LinearLayer(3, 2)  # 3个输入 → 2个输出
print(layer)  # LinearLayer(3 -> 2)

x = [1.0, 2.0, 3.0]
output = layer.forward(x)
print(output)  # [0.06, 0.06]
# 验证:
# y[0] = 0 + 1*0.01 + 2*0.01 + 3*0.01 = 0.06
# y[1] = 0 + 1*0.01 + 2*0.01 + 3*0.01 = 0.06

# ===== 更面向对象的方式(继承) =====
import math

class NeuralNetwork:
    """一个两层的神经网络"""
    
    def __init__(self, input_size, hidden_size, output_size):
        self.layer1 = LinearLayer(input_size, hidden_size)
        self.layer2 = LinearLayer(hidden_size, output_size)
        
    def forward(self, x):
        # 第一层
        h = self.layer1.forward(x)
        # ReLU激活(稍后详解)
        h = [max(0, z) for z in h]
        # 第二层
        y = self.layer2.forward(h)
        return y

net = NeuralNetwork(784, 128, 10)  # 28x28图片 -> 10个数字
print(f'网络结构:{net.layer1} -> ReLU -> {net.layer2}')
知识

NumPy——AI计算的超级引擎

NumPy是Python做科学计算的核心库。它提供了:

  • 多维数组:比Python列表强大得多
  • 快速矩阵运算:底层用C语言和SIMD指令,比纯Python循环快10-100倍
  • 广播:不同形状的数组自动对齐进行计算

为什么不能只用列表?

  • Python列表:存储的是对象的引用,每个元素都是Python对象,内存开销大
  • NumPy数组:存储的是连续内存的原始数值,缓存友好,计算极快

100万维向量点积对比:

  • Python循环:约200毫秒
  • NumPy dot():约2毫秒
  • 快100倍!

神经网络一次前向传播需要数亿次乘加,不用NumPy/PyTorch根本不可能。

代码

NumPy核心操作

import numpy as np

# ===== 创建数组 =====
# 从列表创建
a = np.array([1, 2, 3, 4, 5])
print(a)         # [1 2 3 4 5]
print(a.shape)   # (5,) —— 一维,5个元素

# 二维数组(矩阵)
A = np.array([[1, 2, 3], [4, 5, 6]])
print(A.shape)   # (2, 3) —— 2行3列

# 特殊数组
zeros = np.zeros((2, 3))  # [[0,0,0],[0,0,0]]
ones = np.ones((3, 2))    # [[1,1],[1,1],[1,1]]
eye = np.eye(3)           # 3x3单位矩阵
ar = np.arange(0, 10, 2)  # [0, 2, 4, 6, 8]

# ===== 数组运算 —— 向量化(关键!)=====
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a + b)        # [5, 7, 9]   —— 逐元素加
print(a * b)        # [4, 10, 18] —— 逐元素乘(不是矩阵乘法!)
print(np.sqrt(a))   # [1.0, 1.41, 1.73]

# ===== 点积 / 矩阵乘法 =====
print(np.dot(a, b))     # 1*4+2*5+3*6 = 32 (一维点积)
print(a @ b)            # 32 (@是点积的简写)

# 矩阵乘法
A = np.array([[1, 2], [3, 4]])  # 2x2
B = np.array([[5, 6], [7, 8]])  # 2x2
print(A @ B)
# 结果:
# [[19, 22],
#  [43, 50]]
代码

NumPy高级操作——AI必备

import numpy as np

# ===== 广播(Broadcasting)=====
# 不同形状的数组自动对齐
matrix = np.array([[1, 2, 3], [4, 5, 6]])   # 2x3
bias = np.array([0.1, 0.2, 0.3])             # (3,)
result = matrix + bias                        # 广播:每行都加bias
print(result)
# [[1.1, 2.2, 3.3],
#  [4.1, 5.2, 6.3]]

# ===== 索引和切片 =====
arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(arr[0, 1])      # 2(第0行第1列)
print(arr[:, 1:3])    # 所有行,第1-2列 -> [[2,3],[6,7]]

# ===== 随机数 =====
np.random.seed(42)  # 固定随机种子,保证可复现
rand = np.random.randn(3, 4)  # 标准正态分布随机数
uniform = np.random.rand(2, 3)  # [0,1)均匀分布

# ===== 统计函数 =====
data = np.array([1, 2, 3, 4, 5])
print(np.mean(data))   # 3.0
print(np.std(data))    # 1.414
print(np.max(data))    # 5
print(np.sum(data))    # 15

# ===== 重塑形状 =====
# reshape不改变数据,只改变形状
arr = np.arange(12)        # [0..11]
print(arr.reshape(3, 4))   # 3行4列
print(arr.reshape(2, 2, 3)) # 2个2x3矩阵
实例

向量化 vs 循环——不同在哪?

实例

问题:计算两个长度为1000000的向量的点积。

方法1:Python纯循环

def dot_product_py(a, b):
    result = 0
    for i in range(len(a)):
        result += a[i] * b[i]
    return result

方法2:NumPy向量化

def dot_product_np(a, b):
    return a @ b  # 一行!

结果对比:

  • Python循环:约200毫秒(CPU做100万次Python解释器操作)
  • NumPy:约2毫秒(C语言直接操作内存,加SIMD向量化指令)
  • 差距100倍!

原理:

  • Python循环:每次迭代都要做类型检查、引用计数、分配临时对象
  • NumPy:底层C代码一次处理多个数据,CPU缓存命中率高
  • GPU上差距更大:GPU一次可以做数千次并行乘加
代码

动手实现:用NumPy实现Sigmoid和Softmax

import numpy as np

# ===== Sigmoid =====
def sigmoid(x):
    """Sigmoid激活函数——向量化版本"""
    return 1 / (1 + np.exp(-x))

# 测试
x = np.array([-2, -1, 0, 1, 2])
print('Sigmoid:')
print(sigmoid(x))
# [0.1192, 0.2689, 0.5, 0.7311, 0.8808]

# ===== Softmax =====
def softmax(x):
    """
    Softmax函数——将向量转换为概率分布
    x: 输入向量或矩阵
    返回:概率分布,各元素和为1
    """
    # 数值稳定技巧:减去最大值防止exp溢出
    x_max = np.max(x, axis=-1, keepdims=True)
    exp_x = np.exp(x - x_max)
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)

# 测试
x = np.array([1.0, 2.0, 3.0])
print('\nSoftmax:')
probs = softmax(x)
print(probs)
print('Sum:', np.sum(probs))
# 输出:
# [0.090, 0.245, 0.665]
# Sum: 1.0

# 验证:3最大,对应概率最高(66.5%)

# ===== 预测一个批次的样本 =====
# 假设模型输出(logits) 3个样本,4个类别
logits = np.array([
    [2.0, 1.0, 0.1, 0.5],
    [0.3, 3.0, 0.2, 1.5],
    [1.5, 0.2, 2.5, 0.8]
])
predictions = softmax(logits)
print('\nBatch predictions:')
print(np.round(predictions, 3))
print('\nPredicted classes:', np.argmax(predictions, axis=1))
# [0, 1, 2]  # 三个样本分别预测为类别0,1,2
练习

练习

练习

Q1:写一个函数,输入一个列表,用列表推导式筛选出所有偶数并平方。

Q2:用字典存储学生的三门课成绩:'Alice': [85, 90, 78], 'Bob': [92, 88, 95],计算每个人的平均分。

Q3:用NumPy创建一个3×3的随机矩阵,计算它的转置和所有元素的和。

Q4:写一个类SigmoidLayer,实现Sigmoid激活函数的前向传播。

Q5:用NumPy实现一个函数mse_loss(y_pred, y_true),计算均方误差。

查看答案

A1:

def even_squares(numbers):
    return [x**2 for x in numbers if x % 2 == 0]
print(even_squares([1,2,3,4,5,6]))  # [4, 16, 36]

A2:

scores = {'Alice': [85, 90, 78], 'Bob': [92, 88, 95]}
for name, sc in scores.items():
    avg = sum(sc) / len(sc)
    print(f'{name}: {avg:.1f}')
# Alice: 84.3
# Bob: 91.7

A3:

import numpy as np
np.random.seed(42)
A = np.random.randn(3, 3)
print('Original:', A, sep='\n')
print('Transpose:', A.T, sep='\n')
print('Sum:', np.sum(A))

A4:

class SigmoidLayer:
    def forward(self, x):
        return 1 / (1 + np.exp(-x))
layer = SigmoidLayer()
print(layer.forward(np.array([-1, 0, 1])))

A5:

def mse_loss(y_pred, y_true):
    return np.mean((y_pred - y_true) ** 2)
y_true = np.array([3, 5, 7])
y_pred = np.array([2.5, 5.5, 6.5])
print(mse_loss(y_pred, y_true))  # 0.25