当前位置: 首页 > news >正文

智能识别猫猫

鸡蛋饼是什么猫

今天,有一位爱猫人士找到了我:“9月25日啦,炉石传说重新开服了!”

我:“哦!我知道这个,你是说,我现在该去领金卡了吗?”

爱猫人士:“不!是我看到他们又在讨论狗贼嘘嘘家的猫是什么品种的了!。”

狗贼叔叔是一位知名的老战士,在那样一个简单的下午,他组了一套平时最常用的卡牌,然后普普通通的登了一个顶,然后就再也没有回来。他有一只心爱的小猫叫做“鸡蛋饼”,大概是这样的:

在这里插入图片描述

我:“嗯,然后呢?”

爱猫人士:“我已经受够了有人管它叫做橘猫了!这些人对猫的种类太没有认知,张口就来!”

其实,鸡蛋饼是一只乳色的英短,或者也称为英短金渐层,这也是一个不常见的稀有品种了。英短就是人们常说的“蓝胖子”,这种猫一般都是蓝的或者蓝白的,因此,虽然很多人天天见“蓝胖子”,但是,遇到了鸡蛋饼,就认不出来了,这也不奇怪。

我:”那这不是显得你的水平高嘛?“

爱猫人士:”不,我觉得,你应该写一篇文章,给大家科普一下猫的种类的知识!“

啊这,科普猫的种类?我自己还认不全呢,不过,我想到了一个好办法,我应该训练一个ai,用来识别猫的种类。

资源下载

没有猫,我们也不能凭空变出猫来,因此,首先,我们需要得到一些猫的图片。

猫狗分类:https://www.kaggle.com/datasets/zippyz/cats-and-dogs-breeds-classification-oxford-dataset

比如说,这个数据集看起来不错,直接用这个好了。

我大致查阅了一下,感觉有些不太妙,关于英短的品种
在这里插入图片描述
这全是“蓝胖子”啊,一只英短金渐层也没有,如果就拿这个去给ai训练,到时候认不出鸡蛋饼的可能性非常高啊!

不过,这也正常,鸡蛋饼毕竟是稀有品种,而且这个数据集也有几年的历史了,那个时候,像鸡蛋饼这样的猫比现在还要更稀少,所以没有包括也正常。不过,我并不打算添加额外的图片进去,就先这样好了。

预处理

这个数据集原本叫做“cats and dogs“,肯定不只有猫,那还有狗呢!狗现在可不是我们需要的,首先,我们要想办法把狗删掉,但是,要怎么做呢!我怎么知道谁是猫,谁是狗啊?

还好,数据集的作者早就想到了,猫都是首字母大写的,狗都是首字母小写的,这样的话,我们只需要把首字母大写的图片提取出来就好了。

import os
import shutil
import pandas as pd
from sklearn.model_selection import train_test_splitdataset_path = "images"
cat_folder = os.path.join(dataset_path, "cats")
cat_data = []os.makedirs(cat_folder, exist_ok=True)for filename in os.listdir(dataset_path):if filename.endswith(".jpg"):if filename[0].isupper():shutil.move(os.path.join(dataset_path, filename), os.path.join(cat_folder, filename))for filename in os.listdir(cat_folder):if filename.lower().endswith('.jpg'):filename_no_ext = os.path.splitext(filename)[0]try:cat_breed, number = filename_no_ext.rsplit('_', 1)cat_data.append({'filename': filename, 'breed': cat_breed})except ValueError:print(f"文件名格式不正确: {filename}")cat_df = pd.DataFrame(cat_data)breed_to_num = {breed: idx for idx, breed in enumerate(cat_df["breed"].unique())}
cat_df["breed_num"] = cat_df["breed"].map(breed_to_num)train_df, test_df = train_test_split(df, test_size=0.2, stratify=cat_df["breed_num"], random_state=240925)train_df.to_csv("train_dataset.csv", index=False)
test_df.to_csv("test_dataset.csv", index=False)print("训练集样本数:", len(train_df))
print("测试集样本数:", len(test_df))

模型训练

import torch
import torch.nn as nn
import torchvision.models as models
import pandas as pd
import os
from PIL import Image
from torch.utils.data import Dataset, DataLoader
from torchvision import transformsclass CatDataset(Dataset):def __init__(self, csv_file, img_dir, transform=None):self.data_frame = pd.read_csv(csv_file)self.img_dir = img_dirself.transform = transformdef __len__(self):return len(self.data_frame)def __getitem__(self, idx):img_name = os.path.join(self.img_dir, self.data_frame.iloc[idx, 0])image = Image.open(img_name).convert("RGB")label = int(self.data_frame.iloc[idx, 2])if self.transform:image = self.transform(image)return image, labeltransform = transforms.Compose([transforms.Resize(224, 224),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]),
])train_dataset = CatDataset(csv_file="train_dataset.csv", img_dir="cats", transform=transform)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)test_dataset = CatDataset(csv_file="test_dataset.csv", img_dir="cats", transform=transform)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)class CatClassifier(nn.Module):def __init__(self, num_classes):super(CatClassifier, self).__init__()self.model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)self.model.fc = nn.Linear(self.model.fc.in_features, num_classes)def forward(self, x):return self.model(x)num_classes = len(train_dataset.data_frame["breed_num"].unique())
model = CatClassifier(num_classes)device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)num_epochs = 10for epoch in range(num_epochs):model.train()running_loss = 0.0correct = 0total = 0for images, labels in train_loader:images, labels = images.to(device), labels.to(device)optimizer.zero_grad()outputs = model(images)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item() * images.size(0)_, predicted = torch.max(outputs, 1)total += labels.size(0)correct += (predicted == labels).sum().item()epoch_loss = running_loss / len(train_loader.dataset)epoch_acc = correct / totalprint(f"Epoch [{epoch + 1}/{num_epochs}], Loss: {epoch_loss:.4f}, Accuracy: {epoch_acc:.4f}")

加载与使用

现在,我们已经得到了一个能够识别猫的种类的模型了,要怎么使用呢?

# 注意:你划分的数据集有可能不是这样的,要根据实际情况进行修改
breed_num_to_name = {11: 'British_Shorthair', 0: 'Maine_Coon', 3: 'Sphynx', 10: 'Egyptian_Mau', 2: 'Birman', 8: 'Bombay', 1: 'Siamese', 5: 'Ragdoll', 7: 'Abyssinian', 9: 'Russian_Blue', 4: 'Bengal', 6: 'Persian'}def predict_image(image_path, threshold=0.8):transform = transforms.Compose([transforms.Resize(224, 224)transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225]),])image = Image.open(image_path).convert("RGB")input_image = transform(image).unsqueeze(0).to(device)with torch.no_grad():outputs = model(input_image)probabilities = torch.nn.functional.softmax(outputs, dim=1)max_prob, predicted = torch.max(probabilities, 1)max_prob = max_prob.item()predicted_class = predicted.item()if max_prob >= threshold:predicted_breed = breed_num_to_name.get(predicted_class)print(f"这是一个: {predicted_breed}")else:print("这个我可能不认识~")predict_image("cats/British_Shorthair_241.jpg", threshold=0.8)

最终结果

我们的ai确实已经可以认出测试集中猫的品种了,此时我们拿出来鸡蛋饼的照片:
在这里插入图片描述
我们的ai居然表示:“这个我可能不认识~”,怎么能这样,一定是打开的方式不对!再换一张!
在这里插入图片描述

结果还是一样的,“这个我可能不认识~”。唉,枉费我花费了这么多心血,不过其实也是很正常的事情啦,毕竟我们的训练集中,一张英短金渐层也没,这个时候,想把鸡蛋饼归类为蓝胖子一类,还是太不容易了!

相关链接

普通的一个下午:https://www.bilibili.com/video/BV16w411N7FT/

历史直播回放(up主饕餮):https://space.bilibili.com/10579668/

ai狗贼唱心会很痛:https://www.bilibili.com/video/BV11m411S7rf/

看看鸡蛋饼,突然又有一些伤感,“国服炉石今又在,不见当年叠甲人。”,再排队>15分钟我就要进去了,那位战士说他的补偿不要了,可以给我吗?在这里插入图片描述

相关文章:

  • Day 1 词汇备战
  • 使用MyBatis-Plus与Thymeleaf在Spring Boot中实现增删改查
  • 【CSS】鼠标 、轮廓线 、 滤镜 、 堆叠层级
  • 学习一下怎么用git
  • 阿里云AlibabaCloudLinux php 安装 mysqli 扩展
  • 位运算--(二进制中1的个数)
  • ESP32-定时器中断
  • uniapp vue3 使用echarts绘制图表 柱状图等
  • 缓存穿透 问题(缓存空对象)
  • Java | Leetcode Java题解之第436题寻找右区间
  • Python 如何使用 unittest 模块编写单元测试
  • Vue75 编程式路由导航
  • Azure Data Box 80 TB 现已在中国区正式发布
  • Vue使用axios二次封装、解决跨域问题
  • LabVIEW闪退
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • Centos6.8 使用rpm安装mysql5.7
  • Codepen 每日精选(2018-3-25)
  • Markdown 语法简单说明
  • markdown编辑器简评
  • Python打包系统简单入门
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • Vue组件定义
  • 动态魔术使用DBMS_SQL
  • 关于extract.autodesk.io的一些说明
  • 云大使推广中的常见热门问题
  • 职业生涯 一个六年开发经验的女程序员的心声。
  • hi-nginx-1.3.4编译安装
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • "无招胜有招"nbsp;史上最全的互…
  • #laravel部署安装报错loadFactoriesFrom是undefined method #
  • #Ubuntu(修改root信息)
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (21)起落架/可伸缩相机支架
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (二)Kafka离线安装 - Zookeeper下载及安装
  • (二十三)Flask之高频面试点
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (小白学Java)Java简介和基本配置
  • (转)fock函数详解
  • (转)ORM
  • (转)程序员疫苗:代码注入
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • ./configure,make,make install的作用(转)
  • .NET 8.0 中有哪些新的变化?
  • .Net MVC + EF搭建学生管理系统
  • .net 使用ajax控件后如何调用前端脚本