diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ec4a626 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.pth filter=lfs diff=lfs merge=lfs -text diff --git a/.gitea/workflows/ai.yaml b/.gitea/workflows/ai.yaml index 68db6eb..1788507 100644 --- a/.gitea/workflows/ai.yaml +++ b/.gitea/workflows/ai.yaml @@ -24,7 +24,7 @@ jobs: tags: gitea.webart-tech.ru/webart/beerds/backend:${{ gitea.sha }} - name: Deploy run: | - git clone https://gitea.webart-tech.ru/webart/kuber-deploy.git deploy + git clone https://${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }}@gitea.webart-tech.ru/webart/kuber-deploy.git deploy cd ./deploy/ai sed -i -E 's/backend:.+/backend:${{ gitea.sha }}/g' dogs.yml git config --global user.email "deploy@deploy.deploy" diff --git a/ml/cats.py b/ml/cats.py index 8ef8396..0cd6e95 100644 --- a/ml/cats.py +++ b/ml/cats.py @@ -10,9 +10,9 @@ from train import get_labels, load_model, get_loaders, train, show, DEVICE # ty ImageFile.LOAD_TRUNCATED_IMAGES = True print(f"Using device: {DEVICE}") -IMG_SIZE = (180, 180) +IMG_SIZE = (224, 224) INPUT_DIR = "assets/cat" -NUM_EPOCHS = 10 +NUM_EPOCHS = 50 MODEL_NAME = "cats_model.pth" if __name__ == "__main__": diff --git a/ml/dogs.py b/ml/dogs.py index 6d561aa..0bd531a 100644 --- a/ml/dogs.py +++ b/ml/dogs.py @@ -10,9 +10,9 @@ from train import get_labels, load_model, get_loaders, train, show, DEVICE # ty ImageFile.LOAD_TRUNCATED_IMAGES = True print(f"Using device: {DEVICE}") -IMG_SIZE = (180, 180) +IMG_SIZE = (224, 224) INPUT_DIR = "assets/dog" -NUM_EPOCHS = 100 +NUM_EPOCHS = 50 MODEL_NAME = "dogs_model.pth" if __name__ == "__main__": diff --git a/ml/train.py b/ml/train.py index de48614..649e01e 100644 --- a/ml/train.py +++ b/ml/train.py @@ -17,8 +17,9 @@ def get_labels(input_dir, img_size): transform = transforms.Compose( [ transforms.Resize(img_size), + transforms.RandomHorizontalFlip(), transforms.ToTensor(), - transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)), + transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ] ) dataset = ImageFolder(root=input_dir, transform=transform) @@ -44,7 +45,10 @@ def load_model(model_path: str, labels_dict: dict, device: str = "cuda") -> nn.M if not os.path.isfile(model_path): print("Start new model") model = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT) - model.fc = torch.nn.Linear(model.fc.in_features, len(labels_dict)) + model.fc = nn.Sequential( + nn.Dropout(0.5), # Регуляризация + torch.nn.Linear(model.fc.in_features, len(labels_dict)) + ) return model model = torch.load(model_path, map_location=device, weights_only=False) model.eval() @@ -59,7 +63,7 @@ def train( val_loader: DataLoader, ) -> Tuple[list[float], list[float], list[float], list[float]]: criterion = torch.nn.CrossEntropyLoss() - optimizer = torch.optim.Adam(model.fc.parameters(), lr=0.001, weight_decay=0.001) # type: ignore[union-attr] + optimizer = torch.optim.Adam(model.fc.parameters(), lr=1e-4) # type: ignore[union-attr] # История метрик train_loss_history = [] train_acc_history = [] diff --git a/server/main.py b/server/main.py index a905ac2..45c41af 100644 --- a/server/main.py +++ b/server/main.py @@ -48,7 +48,7 @@ with open("server/meta/labels_cats.json", "r") as f: def predict_image(image, model, device="cuda") -> list[tuple]: - img_size = (180, 180) + img_size = (224, 224) preprocess = transforms.Compose( [ transforms.Resize(img_size), @@ -119,12 +119,16 @@ class BaseController(Controller): path = "/" @get("/") - async def main(self) -> Template: - return Template(name="index.html") + async def dogs(self) -> Template: + return Template(name="dogs.html") @get("/cats") async def cats(self) -> Template: return Template(name="cats.html") + + @get("/contacts") + async def contacts(self) -> Template: + return Template(name="contacts.html") @get("/sitemap.xml", media_type=MediaType.XML) async def sitemaps(self) -> bytes: diff --git a/server/models/cats_model.pth b/server/models/cats_model.pth index 339ce16..1267412 100644 Binary files a/server/models/cats_model.pth and b/server/models/cats_model.pth differ diff --git a/server/models/dogs_model.pth b/server/models/dogs_model.pth index 032efbf..594c972 100644 Binary files a/server/models/dogs_model.pth and b/server/models/dogs_model.pth differ diff --git a/server/static/styles.css b/server/static/styles.css index b83be46..e2f16dd 100644 --- a/server/static/styles.css +++ b/server/static/styles.css @@ -25,7 +25,9 @@ h1 { form { border: 1px solid #696969; - padding: 0 10px 0 10px; + padding: 2px 10px 2px 10px; + display: flex; + justify-content: space-between; } #main { @@ -54,4 +56,66 @@ form { .image-results { text-align: center; -} \ No newline at end of file +} + +#menu { + padding: 0.5rem 0; + border-bottom: 1px solid #dee2e6; /* Серая разделительная линия */ +} + +#menu ul { + list-style-type: none; + margin: 0; + padding: 0; + display: flex; + gap: 2rem; +} + +#menu li { + margin: 0; + padding: 0; +} + +#menu a { + text-decoration: none; + color: #696969; /* Темно-серый цвет */ + font-size: 1.1rem; + transition: color 0.2s ease; + position: relative; +} + +#menu a:hover { + color: #212529; /* Чёрный при наведении */ +} + +/* Стиль для активной ссылки */ +#menu a.active { + color: #212529; + font-weight: 500; +} + +#menu a.active::after { + content: ""; + position: absolute; + width: 100%; + height: 2px; + background-color: #696969; /* Чёрная линия для активной ссылки */ + bottom: -4px; + left: 0; +} + +input[type="submit"] { + padding: 10px; + color: #fff; + background-color: #696969; + cursor: pointer; + } + + input[type="submit"]:hover { + background-color: #212529; + } + + input[type="text"]:hover { + margin: 8px 0; + } + \ No newline at end of file diff --git a/server/templates/base.html b/server/templates/base.html new file mode 100644 index 0000000..ca79912 --- /dev/null +++ b/server/templates/base.html @@ -0,0 +1,44 @@ + + +
+ + + + + {% block meta %}{% endblock %} + +Узнать породу Собаки
-Загрузите фото, чтобы опеределить породу собаки или щенка. Если порода смешанная (или порода определена неточно), после загрузки будет показана вероятность породы животного.
-Определение породы происходит при помощи нейронной сети - точность опеределения составляет 60%, сеть обучена на 65 породах. Если на фото будет неизвестная порода или не кошка - сеть не сможет правильно опеределить, что это.
-Для распознования все фото отправляются на сервер, но там не сохраняются
- -Загрузите фото, чтобы опеределить породу собаки или щенка. Если порода смешанная (или порода определена неточно), после загрузки будет показана вероятность породы животного.
+Определение породы происходит при помощи нейронной сети - точность опеределения составляет 60%, сеть обучена на 65 породах. Если на фото будет неизвестная порода или не кошка - сеть не сможет правильно опеределить, что это.
+Для распознования все фото отправляются на сервер, но там не сохраняются
+{% endblock %} +{% block form %} + +Загрузите фото, чтобы опеределить породу собаки или щенка. Если порода смешанная (или порода определена неточно), после загрузки будет показана вероятность породы животного.
+Определение породы происходит при помощи нейронной сети - точность опеределения составляет 80%, сеть обучена на 125 породах. Если на фото будет неизвестная порода или не собака - сеть не сможет правильно опеределить, что это.
+Для распознования все фото отправляются на сервер, но там не сохраняются
+{% endblock %} +{% block form %} + +Узнать породу Кошки
-Загрузите фото, чтобы опеределить породу собаки или щенка. Если порода смешанная (или порода определена неточно), после загрузки будет показана вероятность породы животного.
-Определение породы происходит при помощи нейронной сети - точность опеределения составляет 60%, сеть обучена на 125 породах. Если на фото будет неизвестная порода или не собака - сеть не сможет правильно опеределить, что это.
-Для распознования все фото отправляются на сервер, но там не сохраняются
- -