Train phase
import json
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import logging
import zipfile
import os
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
device = 'cuda'
with open("/bohr/train-ajis/v2/train.json", "r") as f:
data = list(json.load(f).items())
chars = sorted(list(set("".join([word for word, _ in data]))))
char2idx = {char: idx + 1 for idx, char in enumerate(chars)}
idx2char = {idx: char for char, idx in char2idx.items()}
vocab_size = len(chars)
class CompoundDataset(Dataset):
def __init__(self, data, char2idx):
self.data = data
self.char2idx = char2idx
def __len__(self):
return len(self.data)
def encode(self, word, labels):
return (
torch.tensor([self.char2idx[char] for char in word], dtype=torch.long),
torch.tensor(labels, dtype=torch.float),
)
def __getitem__(self, idx):
word, labels = self.data[idx]
return self.encode(word, labels)
def collate_fn(batch):
inputs, targets = zip(*batch)
lengths = [len(seq) for seq in inputs]
max_len = max(lengths)
padded_inputs = torch.zeros(len(inputs), max_len, dtype=torch.long)
padded_targets = torch.zeros(len(targets), max_len, dtype=torch.float)
for i, (seq, tgt) in enumerate(zip(inputs, targets)):
padded_inputs[i, : len(seq)] = seq
padded_targets[i, : len(tgt)] = tgt
return padded_inputs, padded_targets, lengths
class MyModel(nn.Module):
def __init__(self, vocab_size, hidden_dim=128, num_layers=2):
super(MyModel, self).__init__()
self.vocab_size = vocab_size
self.lstm = nn.LSTM(
vocab_size + 1,
hidden_dim,
num_layers=num_layers,
bidirectional=True,
batch_first=True,
)
self.fc = nn.Linear(hidden_dim * 2, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = nn.functional.one_hot(x, num_classes=self.vocab_size + 1).float()
lstm_out, _ = self.lstm(x)
logits = self.fc(lstm_out)
logits = logits.squeeze(-1)
return self.sigmoid(logits)
def train():
dataset = CompoundDataset(data, char2idx)
batch_size = 128
dataloader = DataLoader(
dataset, batch_size=batch_size, shuffle=True, collate_fn=collate_fn, num_workers=4, prefetch_factor=2
)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = MyModel(vocab_size).to(device)
criterion = nn.BCELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
num_epochs = 32
for epoch in range(num_epochs):
model.train()
epoch_loss = 0
for inputs, targets, lengths in dataloader:
targets = targets.to(device)
optimizer.zero_grad()
outputs = model(inputs.to(device))
mask = torch.arange(inputs.shape[1])[None, :] < torch.tensor(lengths)[:, None]
mask = mask.to(device)
outputs = outputs[mask]
targets = targets[mask]
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
epoch_loss += loss.item()
logging.info(f"Epoch {epoch+1}/{num_epochs}, Loss: {epoch_loss/len(dataloader):.4f}")
return model
model = train()
Save model parameters
Test phase
def predict_and_save(model, input_file, output_file, char2idx, device="cpu"):
"""
Reads a JSON file, predicts segmentation for each word, and saves the results to a new JSON file.
:param model: Trained model
:param input_file: Path to input JSON file
:param output_file: Path to output JSON file
:param char2idx: Character to index mapping
:param device: Device to run the model on
"""
with open(input_file, "r", encoding="utf-8") as f:
data = json.load(f)
predictions = {}
model.eval()
with torch.no_grad():
for word, _ in data.items():
indices = [char2idx.get(char, 0) for char in word]
input_tensor = torch.tensor(indices, dtype=torch.long).unsqueeze(0).to(device)
outputs = model(input_tensor)[0].cpu().numpy()
boundaries = (outputs > 0.6).astype(int)
predictions[word] = boundaries.tolist()
with open(output_file, "w", encoding="utf-8") as f:
json.dump(predictions, f, ensure_ascii=False, indent=4)
logging.info(f"Predictions saved to {output_file}")
if os.environ.get('DATA_PATH'):
data_path = os.environ.get("DATA_PATH") + "/"
else:
print("When the baseline is running, this error message will appear because the test set cannot be read, which is a normal phenomenon.")
input_file = data_path + "val.json"
output_file = "./submissionval.json"
predict_and_save(model, input_file, output_file, char2idx, device)
input_file = data_path + "test.json"
output_file = "./submissiontest.json"
predict_and_save(model, input_file, output_file, char2idx, device)
with zipfile.ZipFile('submission.zip', 'w') as zipf:
zipf.write('submissionval.json')
zipf.write('submissiontest.json')