更新 function.py
Gitea Actions Experiment Test / Experiment-Actions (push) Successful in 22s

This commit is contained in:
2024-11-03 20:34:53 +08:00
parent c1d7442699
commit 2d6addfa3b
+103 -139
View File
@@ -1,170 +1,134 @@
# function.py
import numpy as np import numpy as np
import os import os
from sklearn.preprocessing import StandardScaler # 导入标准化库
""" # 学号和姓名
实验说明:
- 自行编写实现数据加载与 kNN 的手写数字分类算法。
- 函数输入: 待分类的样本 input_vector, 训练数据集 dataSet, 训练集对应的标签 labels。
- 函数返回: 预测的分类标签。
注意事项:
- 在 function.py 中实现 kNN 分类算法。
- 可以自行导入其他库,需要在 requirements.txt 添加依赖,若未在 requirements.txt 中添加使用到的依赖,则无法自动评测,成绩为零。
- 自动评测脚本仅会加载 classify 和 load_data 函数,请勿修改函数名。
- 训练集和测试集需手动下载解压,并放入当前目录下。
测评环境:
- CPU: Intel Xeon(R) CPU E3-1231 v3 @ 3.40GHz
- OS: Ubuntu 22.04.4 LTS
- Python: 3.10.12
- numpy: 2.1.1
"""
"""
请在这里填写学号和姓名,不填写即成绩无效。
"""
id = "202210423119" id = "202210423119"
name = "肖锐" name = "肖锐"
def sigmoid(z):
"""计算sigmoid函数"""
return 1.0 / (1 + np.exp(-z))
""" def compute_gradient(X_batch, y_batch, weights, lambda_reg):
参数说明: """计算梯度,包含L2正则化"""
- input_vector (numpy array): 输入待分类的样本。 z = np.dot(X_batch, weights)
- dataSet (numpy array): 训练数据集。 h = sigmoid(z)
- labels (list or numpy array): 训练集对应的标签。 gradient = np.dot(X_batch.T, (h - y_batch)) / len(y_batch)
- k (int): kNN 算法参数,请**自行设置默认值**,评测时仅会使用其默认值,不修改默认值将**无法通过**评测,**成绩为零**。 gradient += (lambda_reg / len(y_batch)) * weights # L2正则化
返回值: return gradient
- int: 预测的分类标签。
"""
def train_logreg(X, y, learning_rate=0.001, iterations=10000, batch_size=64, lambda_reg=0.1):
"""对率回归训练函数。"""
m, n = X.shape
weights = np.random.randn(n) * np.sqrt(2. / n) # He初始化
print("开始训练对率回归模型")
# 这里写自己的代码实现 for i in range(iterations):
def classify(input_vector, dataSet, labels, k=4): indices = np.random.permutation(m)
""" X_shuffled = X[indices]
KNN 分类算法。 y_shuffled = y[indices]
Args: for j in range(0, m, batch_size):
- input_vector (numpy array): 输入待分类的样本。 X_batch = X_shuffled[j:j + batch_size]
- dataSet (numpy array): 训练数据集。 y_batch = y_shuffled[j:j + batch_size]
- labels (list or numpy array): 训练集对应的标签。 gradient = compute_gradient(X_batch, y_batch, weights, lambda_reg)
- k (int): kNN 算法参数,请**自行设置默认值**,评测时仅会使用其默认值,不修改默认值将**无法通过**评测,**成绩为零**。 weights -= learning_rate * gradient
Returns: if i % 1000 == 0: # 每1000次迭代打印一次信息
- int: 预测的分类标签。 print(f"迭代 {i}: 当前权重形状: {weights.shape}")
"""
"""
使用欧式距离的 kNN 分类算法。
"""
# 计算欧式距离
distances = np.linalg.norm(dataSet - input_vector, axis=1)
# 获取距离最近的 k 个样本的索引 return weights
k_indices = np.argsort(distances)[:k]
# 获取最近邻样本的标签 def predict_logreg(inX, weights):
k_nearest_labels = [labels[i] for i in k_indices] """对率回归预测函数。"""
z = np.dot(inX, weights)
h = sigmoid(z)
return 1 if h >= 0.5 else 0
# 找出出现次数最多的标签 def load_data(filepath):
most_common = max(set(k_nearest_labels), key=k_nearest_labels.count) """从文件中加载数据。"""
if not os.path.exists(filepath):
raise FileNotFoundError(f"文件 {filepath} 不存在!")
return most_common data, labels = [], []
print(f"加载数据中: {filepath}")
with open(filepath, 'r') as f:
for line in f:
line = line.strip().split()
if len(line) >= 1 and line[-1].isdigit():
line_data = [0 if x == '?' else float(x) for x in line[:-1]]
data.append(line_data)
labels.append(int(line[-1]))
data = np.array(data)
labels = np.array(labels)
# 这里也写自己的代码实现 num_features = data.shape[1]
indices_to_remove = [2] # 去掉特征3(索引2)
def load_dataset(directory): if num_features > 23:
""" indices_to_remove.extend(range(23, min(28, num_features)))
从文件夹中加载数据,若不自行正确实现,则无法通过评测,**不记录成绩**。
Args: data = np.delete(data, indices_to_remove, axis=1)
- directory (str): 数据文件夹路径。 target_variable = np.where((labels == 2) | (labels == 3), 0, 1)
Returns: print(f"数据加载成功,数据形状: {data.shape}, 标签形状: {target_variable.shape}")
- np.array: 特征数据。 return data, target_variable
- np.array: 标签数据。
"""
features = []
labels = []
# 遍历文件夹中的所有文件 def k_fold_cross_validation(X, y, k=5, learning_rate=0.001, iterations=10000, batch_size=64, lambda_reg=0.1):
for filename in os.listdir(directory): """K折交叉验证。"""
if filename.endswith(".txt"): # 假设文件是 .txt 格式 m = X.shape[0]
filepath = os.path.join(directory, filename) fold_size = m // k
accuracies = []
# 假设文件名格式为 "数字_编号.txt" for i in range(k):
label = int(filename.split('_')[0]) val_start = i * fold_size
val_end = (i + 1) * fold_size if i != k - 1 else m
# 加载文件中的二进制数据并转换为 1D 向量 X_val = X[val_start:val_end]
image_vector = img_to_vector(filepath) y_val = y[val_start:val_end]
X_train = np.concatenate((X[:val_start], X[val_end:]))
y_train = np.concatenate((y[:val_start], y[val_end:]))
features.append(image_vector) weights = train_logreg(X_train, y_train, learning_rate, iterations, batch_size, lambda_reg)
labels.append(label)
return np.array(features), np.array(labels) val_accuracy = np.mean([predict_logreg(X_val[j], weights) == y_val[j] for j in range(len(y_val))])
accuracies.append(val_accuracy)
print(f"Fold {i + 1}/{k} - 验证准确率: {val_accuracy:.4f}")
# 这里还写自己的代码实现 return np.mean(accuracies)
def img_to_vector(filename):
"""
将二值图像转换为向量。
该部分应为 load_dataset 函数的辅助子函数,可单独在这里实现,也可直接集成至 load_dataset 函数中。
Args:
- filename (str): 图像文件路径。
Returns:
- np.array: 二值图像向量。
"""
with open(filename) as file:
lines = file.readlines()
image_vector = np.array([int(char) for line in lines for char in line.strip()])
return image_vector
if __name__ == "__main__": if __name__ == "__main__":
# 以下代码可用于测试,可自行随意修改 # 加载训练集
train_data, train_labels = load_dataset("trainingDigits") try:
X_train, y_train = load_data("./data/horse-colic.data")
except FileNotFoundError as e:
print(e)
exit()
test_data = ( # 数据归一化处理
"00000000000000110000000000000000" scaler = StandardScaler()
"00000000000011111000000000000000" X_train = scaler.fit_transform(X_train) # 使用训练集进行标准化
"00000000000011111100000000000000"
"00000000001111111110000000000000"
"00000000001111111110000000000000"
"00000000001111111100000000000000"
"00000000011111111000000000000000"
"00000000111111111000000000000000"
"00000000111111110000000000000000"
"00000000111111110000000000000000"
"00000000111111110000000000000000"
"00000000111111110000000000000000"
"00000000111111100000000000000000"
"00000001111111100000000000000000"
"00000001111111100000000000000000"
"00000001111111100000000000000000"
"00000001111111111111000000000000"
"00000001111111111111111000000000"
"00000001111111111111111100000000"
"00000011111111111111111110000000"
"00000011111111111111111110000000"
"00000001111111111111111110000000"
"00000001111111111111111110000000"
"00000000111111111100111111100000"
"00000000111111111000111111100000"
"00000000011111111001111111100000"
"00000000011111111111111111000000"
"00000000001111111111111111000000"
"00000000000111111111111111000000"
"00000000000011111111111100000000"
"00000000000001111111110000000000"
"00000000000000000111100000000000"
)
predicted_label = classify(
np.array([int(char) for char in test_data]).reshape(1, 1024),
train_data,
train_labels,
)
print(f"预测的标签: {predicted_label}") # k折交叉验证
avg_accuracy = k_fold_cross_validation(X_train, y_train, k=5, learning_rate=0.01, iterations=1000)
print(f"K折交叉验证的平均准确率: {avg_accuracy:.4f}")
# 加载测试集
try:
X_test, y_test = load_data("./data/horse-colic.test")
except FileNotFoundError as e:
print(e)
exit()
# 测试集也需要进行相同的归一化
X_test = scaler.transform(X_test) # 使用相同的Scaler进行测试集的标准化
# 训练对率回归模型
weights = train_logreg(X_train, y_train, learning_rate=0.01, iterations=1000)
# 测试单个数据
y_pred = predict_logreg(X_test[11], weights)
print(f"预测结果: {y_pred}, 实际结果: {y_test[11]}")