# "cuda" only when GPUs are available.
device = "cuda" if torch.cuda.is_available() else "cpu"
_exp_name = "sample"
# The number of training epochs and patience.
n_epochs = 4
patience = 300 # If no improvement in 'patience' epochs, early stop
# Initialize a model, and put it on the device specified.
model = Classifier().to(device)
# For the classification task, we use cross-entropy as the measurement of performance.
criterion = nn.CrossEntropyLoss()
# Initialize optimizer, you may fine-tune some hyperparameters such as learning rate on your own.
optimizer = torch.optim.Adam(model.parameters(), lr=0.0003, weight_decay=1e-5)
# Initialize trackers, these are not parameters and should not be changed
stale = 0
best_acc = 0
for epoch in range(n_epochs):
# ---------- Training ----------
# Make sure the model is in train mode before training.
model.train()
# These are used to record information in training.
train_loss = []
train_accs = []
for batch in tqdm(train_loader):
# A batch consists of image data and corresponding labels.
imgs, labels = batch
#imgs = imgs.half()
#print(imgs.shape,labels.shape)
# Forward the data. (Make sure data and model are on the same device.)
logits = model(imgs.to(device))
# Calculate the cross-entropy loss.
# We don't need to apply softmax before computing cross-entropy as it is done automatically.
loss = criterion(logits, labels.to(device))
# Gradients stored in the parameters in the previous step should be cleared out first.
optimizer.zero_grad()
# Compute the gradients for parameters.
loss.backward()
# Clip the gradient norms for stable training.
grad_norm = nn.utils.clip_grad_norm_(model.parameters(), max_norm=10)
# Update the parameters with computed gradients.
optimizer.step()
# Compute the accuracy for current batch.
# 取全连接层最后输出的概率最大值作为预测的类别
acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()
# Record the loss and accuracy.
train_loss.append(loss.item())
train_accs.append(acc)
train_loss = sum(train_loss) / len(train_loss)
train_acc = sum(train_accs) / len(train_accs)
# Print the information.
print(f"[ Train | {epoch + 1:03d}/{n_epochs:03d} ] loss = {train_loss:.5f}, acc = {train_acc:.5f}")
# ---------- Validation ----------
# Make sure the model is in eval mode so that some modules like dropout are disabled and work normally.
model.eval()
# These are used to record information in validation.
valid_loss = []
valid_accs = []
# Iterate the validation set by batches.
for batch in tqdm(valid_loader):
# A batch consists of image data and corresponding labels.
imgs, labels = batch
#imgs = imgs.half()
# We don't need gradient in validation.
# Using torch.no_grad() accelerates the forward process.
with torch.no_grad():
logits = model(imgs.to(device))
# We can still compute the loss (but not the gradient).
loss = criterion(logits, labels.to(device))
# Compute the accuracy for current batch.
acc = (logits.argmax(dim=-1) == labels.to(device)).float().mean()
# Record the loss and accuracy.
valid_loss.append(loss.item())
valid_accs.append(acc)
#break
# The average loss and accuracy for entire validation set is the average of the recorded values.
valid_loss = sum(valid_loss) / len(valid_loss)
valid_acc = sum(valid_accs) / len(valid_accs)
# Print the information.
print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")
# update logs
if valid_acc > best_acc:
with open(f"./{_exp_name}_log.txt","a"):
print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f} -> best")
else:
with open(f"./{_exp_name}_log.txt","a"):
print(f"[ Valid | {epoch + 1:03d}/{n_epochs:03d} ] loss = {valid_loss:.5f}, acc = {valid_acc:.5f}")
# save models
if valid_acc > best_acc:
print(f"Best model found at epoch {epoch}, saving model")
torch.save(model.state_dict(), f"{_exp_name}_best.ckpt") # only save best to prevent output memory exceed error
best_acc = valid_acc
stale = 0
else:
stale += 1
if stale > patience:
print(f"No improvment {patience} consecutive epochs, early stopping")
break
针对如何制作图像分类实验结果的可视化热力图,以下是解决方案:
def forward(self, x):
out = self.cnn(x)
out = out.view(out.size()[0], -1)
logits = self.fc(out)
probs = nn.functional.softmax(logits, dim=1)
return probs
import pandas as pd
import os
from PIL import Image
from torchvision import transforms
def get_image_probs(model, image_path):
transform = transforms.Compose([
transforms.Resize(size=(128, 128)),
transforms.ToTensor(),
])
image = Image.open(image_path)
inputs = transform(image).unsqueeze(0)
probs = model(inputs)
return probs.detach().numpy()[0]
def get_classification_results(model, data_directory):
results = []
for class_name in sorted(os.listdir(data_directory)):
class_directory = os.path.join(data_directory, class_name)
for image_name in os.listdir(class_directory):
image_path = os.path.join(class_directory, image_name)
probs = get_image_probs(model, image_path)
result = {"ImageName": image_name, "ClassName": class_name}
for class_idx, class_prob in enumerate(probs):
result[f"Class{class_idx}Prob"] = class_prob
results.append(result)
return pd.DataFrame(results)
model = Classifier()
result_df = get_classification_results(model, data_directory)
上述代码将每个图像的分类概率存储在一个DataFrame中,每一行表示一个图像,包含ImageName(图像文件名)、ClassName(类别名)和每个类别的概率。结果可以按照需要进行处理和分析。 5. 使用pandas的pivot_table函数将分类结果转换为热力图的输入格式。以下代码示例将结果转换为以Class为列、ImageName为行、ClassProb为值的DataFrame,然后使用matplotlib的imshow函数生成热力图。
result_pivot = result_df.pivot_table(
index="ImageName",
columns="ClassName",
values=[f"Class{i}Prob" for i in range(11)],
)
result_pivot = result_pivot.reindex(sorted(result_pivot.columns), axis=1)
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 10))
ax.imshow(result_pivot, aspect="auto", cmap="jet")
ax.set_yticks(range(len(result_pivot)))
ax.set_xticks(range(len(result_pivot.columns)))
ax.set_yticklabels(result_pivot.index)
ax.set_xticklabels(result_pivot.columns, rotation=90)
ax.set_xlabel("Class")
ax.set_ylabel("ImageName")
plt.show()
上述代码中,cmap参数指定使用jet颜色映射,aspect参数指定热力图的宽高比例为1:1,这样热力图就不会因为图片名称太长而变形。set_yticks和set_xticks方法用于设置刻度位置,set_yticklabels和set_xticklabels方法用于设置刻度标签。最终用show方法显示热力图。
综上所述,以上是制作图像分类实验结果的可视化热力图的具体解决方案。