手写数字识别

This commit is contained in:
yukun-hh 2026-07-05 00:31:14 +08:00
parent 9d1968c020
commit 355e01a4a1
7 changed files with 98520 additions and 8 deletions

View file

@ -100,14 +100,6 @@
],
"execution_count": 241
},
{
"metadata": {},
"cell_type": "code",
"source": "",
"id": "36468cd80d626c74",
"outputs": [],
"execution_count": null
},
{
"metadata": {},
"cell_type": "code",

View file

@ -0,0 +1,207 @@
# %%
import os
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
import torch
from torch.utils.data import Dataset, DataLoader
import pandas as pd
import transformers
from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments
from tqdm import tqdm
# %%
train_df = pd.read_csv('./train.csv')
test_df = pd.read_csv('./test.csv')
from sklearn.model_selection import train_test_split
train_df, val_df = train_test_split(train_df, test_size=0.1, random_state=42, stratify=train_df['label'])
print(f"Train: {len(train_df)}, Val: {len(val_df)}, Test: {len(test_df)}")
# %%
train_df.head()
# %%
model_name = "xlm-roberta-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name, num_labels=3)
# %%
embedded_text=tokenizer("你好啊")
# %%
tokenizer.decode(embedded_text['input_ids'],skip_special_tokens=True)
# %%
embedded_text=tokenizer(["你好啊","我是灰太狼"],["我不好","我是红太狼"])
embedded_text=tokenizer("你好啊",return_tensors='pt')
# %%
class NliDataset(Dataset):
def __init__(self, df, tokenizer, max_length=128):
self.df = df.reset_index(drop=True)
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
row = self.df.iloc[idx]
premise = str(row['premise'])
hypothesis = str(row['hypothesis'])
# 编码文本对,返回 input_ids, attention_mask
encoding = self.tokenizer(
premise,
hypothesis,
truncation=True,
padding='max_length',
max_length=self.max_length,
return_tensors='pt' # 返回 PyTorch Tensor
)
# 去掉 batch 维度(因为只处理单条)
item = {
'input_ids': encoding['input_ids'].squeeze(0),
'attention_mask': encoding['attention_mask'].squeeze(0)
}
if 'label' in row:
item['labels'] = torch.tensor(row['label'], dtype=torch.long)
return item
# %%
# %%
batch_size = 16 # 根据显存调整,推荐使用 16 或 32
max_length = 128
train_dataset = NliDataset(train_df, tokenizer, max_length)
val_dataset = NliDataset(val_df, tokenizer, max_length)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False)
# %%
# %%
from transformers import get_linear_schedule_with_warmup
from sklearn.metrics import accuracy_score
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-5)
# 计算总训练步数(用于 warmup)
total_steps = len(train_loader) * 3 # 假设训练 3 个 epoch
warmup_steps = int(0.1 * total_steps) # warmup 比例为 10%
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps=warmup_steps,
num_training_steps=total_steps
)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)
# %%
def evaluate(model, data_loader, device):
model.eval()
all_preds = []
all_labels = []
with torch.no_grad():
for batch in tqdm(data_loader, desc="Evaluating"):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
outputs = model(input_ids, attention_mask=attention_mask)
logits = outputs.logits
preds = torch.argmax(logits, dim=1)
all_preds.extend(preds.cpu().numpy())
all_labels.extend(labels.cpu().numpy())
acc = accuracy_score(all_labels, all_preds)
return acc
# %%
num_epochs = 3
best_val_acc = 0.0
for epoch in range(num_epochs):
model.train()
total_loss = 0
progress_bar = tqdm(train_loader, desc=f'Epoch {epoch+1}/{num_epochs}')
for batch in progress_bar:
# 将数据移至设备
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
labels = batch['labels'].to(device)
# 前向传播
outputs = model(input_ids, attention_mask=attention_mask, labels=labels)
loss = outputs.loss
# 反向传播
loss.backward()
# 梯度裁剪(防止梯度爆炸)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 更新参数
optimizer.step()
scheduler.step() # 更新学习率
optimizer.zero_grad()
# 记录损失
total_loss += loss.item()
progress_bar.set_postfix({'loss': loss.item()})
avg_train_loss = total_loss / len(train_loader)
print(f"Epoch {epoch+1} - Average Train Loss: {avg_train_loss:.4f}")
# 在每个 epoch 结束后评估验证集
val_acc = evaluate(model, val_loader, device)
print(f"Epoch {epoch+1} - Validation Accuracy: {val_acc:.4f}")
# 保存最佳模型
if val_acc > best_val_acc:
best_val_acc = val_acc
torch.save(model.state_dict(), 'best_model.pt')
print("Best model saved!")
# %%
# 加载最佳模型权重
model.load_state_dict(torch.load('best_model.pt'))
model.eval()
# 构建测试集 Dataset 和 DataLoader(注意测试集没有 label)
class TestDataset(Dataset):
def __init__(self, df, tokenizer, max_length=128):
self.df = df.reset_index(drop=True)
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
row = self.df.iloc[idx]
premise = str(row['premise'])
hypothesis = str(row['hypothesis'])
encoding = self.tokenizer(
premise,
hypothesis,
truncation=True,
padding='max_length',
max_length=self.max_length,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].squeeze(0),
'attention_mask': encoding['attention_mask'].squeeze(0)
}
test_dataset = TestDataset(test_df, tokenizer, max_length)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
# 预测
all_preds = []
with torch.no_grad():
for batch in tqdm(test_loader, desc="Predicting"):
input_ids = batch['input_ids'].to(device)
attention_mask = batch['attention_mask'].to(device)
outputs = model(input_ids, attention_mask=attention_mask)
logits = outputs.logits
preds = torch.argmax(logits, dim=1)
all_preds.extend(preds.cpu().numpy())
# 生成提交文件
submission = pd.DataFrame({
'id': test_df['id'],
'label': all_preds
})
submission.to_csv('submission.csv', index=False)
print("Submission saved!")

216
digit-recognizer/main.ipynb Normal file
View file

@ -0,0 +1,216 @@
{
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.13",
"mimetype": "text/x-python",
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"pygments_lexer": "ipython3",
"nbconvert_exporter": "python",
"file_extension": ".py"
}
},
"nbformat_minor": 4,
"nbformat": 4,
"cells": [
{
"cell_type": "code",
"source": "import matplotlib.pyplot as plt\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nimport pandas as pd",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:12.326817130Z",
"start_time": "2026-07-03T10:17:12.293250353Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:09.940312Z",
"iopub.execute_input": "2026-07-04T16:16:09.940612Z",
"iopub.status.idle": "2026-07-04T16:16:12.856395Z",
"shell.execute_reply.started": "2026-07-04T16:16:09.940586Z",
"shell.execute_reply": "2026-07-04T16:16:12.855776Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "%cd /kaggle/working\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\ntrain_df=pd.read_csv(\"/kaggle/input/competitions/digit-recognizer/train.csv\")\ntest_df=pd.read_csv(\"/kaggle/input/competitions/digit-recognizer/test.csv\")",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:15.014258006Z",
"start_time": "2026-07-03T10:17:12.346500913Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:12.857730Z",
"iopub.execute_input": "2026-07-04T16:16:12.858065Z",
"iopub.status.idle": "2026-07-04T16:16:16.048768Z",
"shell.execute_reply.started": "2026-07-04T16:16:12.858040Z",
"shell.execute_reply": "2026-07-04T16:16:16.048163Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "fig, ax = plt.subplots(nrows=2, ncols=2, sharex='all', sharey='all')\nax = ax.flatten()\nfor i in range(4):\n img = train_df.iloc[i][1:].to_numpy().reshape(28,28)\n # ax[i].imshow(img,cmap='Greys')\n ax[i].imshow(img)\n ax[i].set_title(f'{train_df.iloc[i][0]}')",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:15.565961732Z",
"start_time": "2026-07-03T10:17:15.073738808Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:16.049873Z",
"iopub.execute_input": "2026-07-04T16:16:16.050089Z",
"iopub.status.idle": "2026-07-04T16:16:16.369380Z",
"shell.execute_reply.started": "2026-07-04T16:16:16.050066Z",
"shell.execute_reply": "2026-07-04T16:16:16.368725Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "class DatasetMnist(Dataset):\n def __init__(self,df):\n self.df=df\n def __len__(self):\n return len(self.df)\n def __getitem__(self, idx):\n item= {\"Data\": torch.tensor(self.df.iloc[idx][1:].to_numpy().reshape(28, 28),dtype=torch.float), \"label\": torch.tensor(self.df.iloc[idx][0],dtype=torch.long)}\n return item\nbatch_size =64\ntrain_dataset = DatasetMnist(train_df)\ntrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:15.669173315Z",
"start_time": "2026-07-03T10:17:15.616933550Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:16.370253Z",
"iopub.execute_input": "2026-07-04T16:16:16.370426Z",
"iopub.status.idle": "2026-07-04T16:16:16.375630Z",
"shell.execute_reply.started": "2026-07-04T16:16:16.370409Z",
"shell.execute_reply": "2026-07-04T16:16:16.375171Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "class MnistModule(nn.Module):\n def __init__(self):\n super(MnistModule, self).__init__()\n self.fc1 = nn.Linear(28*28, 512)\n self.relu = nn.ReLU()\n self.fc2 = nn.Linear(512, 256)\n self.fc3 = nn.Linear(256, 128)\n self.fc4 = nn.Linear(128,10)\n def forward(self,X):\n return self.fc4(self.relu(self.fc3(self.relu(self.fc2(self.relu(self.fc1(X.view(-1,28*28))))))))",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:15.729366448Z",
"start_time": "2026-07-03T10:17:15.670668930Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:16.376258Z",
"iopub.execute_input": "2026-07-04T16:16:16.376401Z",
"iopub.status.idle": "2026-07-04T16:16:16.389616Z",
"shell.execute_reply.started": "2026-07-04T16:16:16.376386Z",
"shell.execute_reply": "2026-07-04T16:16:16.388703Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "model = MnistModule()\nmodel=model.to(device)\nloss_func = nn.CrossEntropyLoss()\noptimizer = torch.optim.AdamW(model.parameters(), lr=2e-4)\nscheduler=torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)\n",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:17:15.792463697Z",
"start_time": "2026-07-03T10:17:15.730536380Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:16.390304Z",
"iopub.execute_input": "2026-07-04T16:16:16.390504Z",
"iopub.status.idle": "2026-07-04T16:16:18.580638Z",
"shell.execute_reply.started": "2026-07-04T16:16:16.390482Z",
"shell.execute_reply": "2026-07-04T16:16:18.580038Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "epochs = 30\nfor epoch in range(epochs):\n model.train()\n training_loss=0\n for batch in train_loader:\n optimizer.zero_grad()\n X = batch['Data']\n labels=batch['label']\n X=X.to(device)\n labels=labels.to(device)\n #print(model(X),'\\n',labels)\n loss = loss_func(model(X),labels)\n loss.backward()\n optimizer.step()\n training_loss+=loss.item()\n scheduler.step()\n print(f\"train_loss: {training_loss/len(train_loader)}\")",
"metadata": {
"ExecuteTime": {
"end_time": "2026-07-03T10:18:55.758148762Z",
"start_time": "2026-07-03T10:17:31.736342843Z"
},
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:16:18.582512Z",
"iopub.execute_input": "2026-07-04T16:16:18.582874Z",
"iopub.status.idle": "2026-07-04T16:18:53.082304Z",
"shell.execute_reply.started": "2026-07-04T16:16:18.582856Z",
"shell.execute_reply": "2026-07-04T16:18:53.081057Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": [
"class DatasetMnistTest(Dataset):\n",
" def __init__(self,df):\n",
" self.df=df\n",
" def __len__(self):\n",
" return len(self.df)\n",
" def __getitem__(self, idx):\n",
" item= {\"Data\": torch.tensor(self.df.iloc[idx].to_numpy().reshape(28, 28),dtype=torch.float)}\n",
" return item\n",
"test_dataset = DatasetMnistTest(test_df)\n",
"test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)\n",
"all_preds = []\n",
"with torch.no_grad():\n",
" for batch in test_loader:\n",
" input_ids = batch['Data'].to(device)\n",
" outputs = model(input_ids)\n",
" preds = torch.argmax(outputs, dim=1)\n",
" all_preds.extend(preds.cpu().numpy())\n",
"idd = range(1,len(all_preds)+1)\n",
"submission = pd.DataFrame({\n",
" 'ImageId':idd,\n",
" 'Label': all_preds\n",
"})\n",
"print(submission)\n",
"submission.to_csv('submission.csv', index=False)\n",
"print(\"Submission saved!\")"
],
"metadata": {
"trusted": true,
"execution": {
"iopub.status.busy": "2026-07-04T16:19:40.600448Z",
"iopub.execute_input": "2026-07-04T16:19:40.601006Z",
"iopub.status.idle": "2026-07-04T16:19:41.910494Z",
"shell.execute_reply.started": "2026-07-04T16:19:40.600982Z",
"shell.execute_reply": "2026-07-04T16:19:41.909862Z"
}
},
"outputs": [],
"execution_count": null
},
{
"cell_type": "code",
"source": "",
"metadata": {
"trusted": true
},
"outputs": [],
"execution_count": null
}
]
}

94
digit-recognizer/main.py Normal file
View file

@ -0,0 +1,94 @@
# %%
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import pandas as pd
# %%
# %cd /kaggle/working
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
train_df=pd.read_csv("/kaggle/input/competitions/digit-recognizer/train.csv")
test_df=pd.read_csv("/kaggle/input/competitions/digit-recognizer/test.csv")
# %%
fig, ax = plt.subplots(nrows=2, ncols=2, sharex='all', sharey='all')
ax = ax.flatten()
for i in range(4):
img = train_df.iloc[i][1:].to_numpy().reshape(28,28)
# ax[i].imshow(img,cmap='Greys')
ax[i].imshow(img)
ax[i].set_title(f'{train_df.iloc[i][0]}')
# %%
class DatasetMnist(Dataset):
def __init__(self,df):
self.df=df
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
item= {"Data": torch.tensor(self.df.iloc[idx][1:].to_numpy().reshape(28, 28),dtype=torch.float), "label": torch.tensor(self.df.iloc[idx][0],dtype=torch.long)}
return item
batch_size =64
train_dataset = DatasetMnist(train_df)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
# %%
class MnistModule(nn.Module):
def __init__(self):
super(MnistModule, self).__init__()
self.fc1 = nn.Linear(28*28, 512)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(512, 256)
self.fc3 = nn.Linear(256, 128)
self.fc4 = nn.Linear(128,10)
def forward(self,X):
return self.fc4(self.relu(self.fc3(self.relu(self.fc2(self.relu(self.fc1(X.view(-1,28*28))))))))
# %%
model = MnistModule()
model=model.to(device)
loss_func = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(), lr=2e-4)
scheduler=torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=10)
# %%
epochs = 30
for epoch in range(epochs):
model.train()
training_loss=0
for batch in train_loader:
optimizer.zero_grad()
X = batch['Data']
labels=batch['label']
X=X.to(device)
labels=labels.to(device)
#print(model(X),'\n',labels)
loss = loss_func(model(X),labels)
loss.backward()
optimizer.step()
training_loss+=loss.item()
scheduler.step()
print(f"train_loss: {training_loss/len(train_loader)}")
# %%
class DatasetMnistTest(Dataset):
def __init__(self,df):
self.df=df
def __len__(self):
return len(self.df)
def __getitem__(self, idx):
item= {"Data": torch.tensor(self.df.iloc[idx].to_numpy().reshape(28, 28),dtype=torch.float)}
return item
test_dataset = DatasetMnistTest(test_df)
test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False)
all_preds = []
with torch.no_grad():
for batch in test_loader:
input_ids = batch['Data'].to(device)
outputs = model(input_ids)
preds = torch.argmax(outputs, dim=1)
all_preds.extend(preds.cpu().numpy())
idd = range(1,len(all_preds)+1)
submission = pd.DataFrame({
'ImageId':idd,
'Label': all_preds
})
print(submission)
submission.to_csv('submission.csv', index=False)
print("Submission saved!")
# %%

File diff suppressed because it is too large Load diff

28001
digit-recognizer/test.csv Normal file

File diff suppressed because one or more lines are too long

42001
digit-recognizer/train.csv Normal file

File diff suppressed because one or more lines are too long