为什么学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.7A3:
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