Merge remote-tracking branch 'gitee/detached'
# Conflicts: # classifier/embedding_food_classifier_app.py # faiss_vector_db/build_faiss_index.py # faiss_vector_db/visualize_embeddings.py # train/train_embedding.py
This commit is contained in:
@@ -65,13 +65,15 @@ class EmbeddingFoodClassifierApp:
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
# model_path = "../model/embedding_20251011_133653/best_embedding_model.pth"
|
||||
# model_path = os.path.join(BASE_DIR, "../model/embedding_20251011_133653/best_embedding_model.pth")
|
||||
model_path = os.path.join(BASE_DIR, "../model/WholeIngredientRecognition/embedding_20251023_120531/best_embedding_model.pth")
|
||||
model_path = os.path.join(BASE_DIR, "../model/ProcessedIngredientRecognition/embedding_20251029_100204/best_embedding_model.pth")
|
||||
# model_path = os.path.join(BASE_DIR, "../model/WholeIngredientRecognition/embedding_20251024_091151/best_embedding_model.pth")
|
||||
# model_path = os.path.join(BASE_DIR, "../model/DishClassification/embedding_20251022_093635/best_embedding_model.pth")
|
||||
|
||||
# FAISS索引目录
|
||||
# index_dir = "../faiss_vector_db/faiss_index"
|
||||
# index_dir = os.path.join(BASE_DIR, "../faiss_vector_db/faiss_index")
|
||||
index_dir = os.path.join(BASE_DIR, "../faiss_vector_db/WholeIngredientRecognition/faiss_index102402")
|
||||
index_dir = os.path.join(BASE_DIR, "../faiss_vector_db/ProcessedIngredientRecognition/faiss_index")
|
||||
index_dir = os.path.join(BASE_DIR, "../faiss_vector_db/WholeIngredientRecognition/faiss_index")
|
||||
# index_dir = os.path.join(BASE_DIR, "../faiss_vector_db/DishClassification/faiss_index")
|
||||
|
||||
if os.path.exists(model_path) and os.path.exists(index_dir):
|
||||
|
||||
@@ -480,11 +480,14 @@ def main():
|
||||
"""主函数"""
|
||||
# 配置参数
|
||||
# MODEL_PATH = "../model/embedding_20251011_133653/best_embedding_model.pth"
|
||||
MODEL_PATH = "../model/WholeIngredientRecognition/embedding_20251021_085915/best_embedding_model.pth"
|
||||
MODEL_PATH = "../model/ProcessedIngredientRecognition/embedding_20251029_100204/best_embedding_model.pth"
|
||||
# MODEL_PATH = "../model/WholeIngredientRecognition/embedding_20251024_091151/best_embedding_model.pth"
|
||||
# MODEL_PATH = "../model/DishClassification/embedding_20251022_093635/best_embedding_model.pth"
|
||||
TRAIN_DIR = "../dataset/WholeIngredientRecognition/train"
|
||||
TRAIN_DIR = "../dataset/ProcessedIngredientRecognition/train"
|
||||
# TRAIN_DIR = "../dataset/WholeIngredientRecognition/train"
|
||||
# TRAIN_DIR = "../dataset/DishClassification/train"
|
||||
OUTPUT_DIR = "WholeIngredientRecognition/faiss_index"
|
||||
OUTPUT_DIR = "ProcessedIngredientRecognition/faiss_index"
|
||||
# OUTPUT_DIR = "WholeIngredientRecognition/faiss_index"
|
||||
# OUTPUT_DIR = "DishClassification/faiss_index"
|
||||
BATCH_SIZE = 16
|
||||
INDEX_TYPE = 'flat' # 'flat', 'ivf', 'hnsw'
|
||||
|
||||
@@ -163,9 +163,11 @@ def plot_2d(Z: np.ndarray, y: np.ndarray, title: str, out_path: Optional[str] =
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="可视化高维 embedding 并进行坍塌诊断")
|
||||
# parser.add_argument("--embeddings", type=str, default=os.path.join("DishClassification/faiss_index", "embeddings.json"), help="embeddings.json 路径")
|
||||
parser.add_argument("--embeddings", type=str, default=os.path.join("WholeIngredientRecognition/faiss_index", "embeddings.json"), help="embeddings.json 路径")
|
||||
# parser.add_argument("--embeddings", type=str, default=os.path.join("WholeIngredientRecognition/faiss_index", "embeddings.json"), help="embeddings.json 路径")
|
||||
parser.add_argument("--embeddings", type=str, default=os.path.join("ProcessedIngredientRecognition/faiss_index", "embeddings.json"), help="embeddings.json 路径")
|
||||
# parser.add_argument("--labels", type=str, default=os.path.join("DishClassification/faiss_index", "labels.json"), help="labels.json 路径")
|
||||
parser.add_argument("--labels", type=str, default=os.path.join("WholeIngredientRecognition/faiss_index", "labels.json"), help="labels.json 路径")
|
||||
# parser.add_argument("--labels", type=str, default=os.path.join("WholeIngredientRecognition/faiss_index", "labels.json"), help="labels.json 路径")
|
||||
parser.add_argument("--labels", type=str, default=os.path.join("ProcessedIngredientRecognition/faiss_index", "labels.json"), help="labels.json 路径")
|
||||
parser.add_argument("--method", type=str, default="pca", choices=["pca", "tsne", "umap"], help="降维方法")
|
||||
parser.add_argument("--seed", type=int, default=42)
|
||||
parser.add_argument("--max_points", type=int, default=None, help="抽样上限,避免t-SNE/UMAP过慢;None为全量")
|
||||
|
||||
+18
-69
@@ -32,7 +32,6 @@ class TaskConfig:
|
||||
name: str
|
||||
train_dir: str
|
||||
val_dir: str
|
||||
unknown_dir: str # 未知类别数据目录
|
||||
embedding_dim: int
|
||||
batch_size: int
|
||||
lr: float
|
||||
@@ -46,7 +45,6 @@ TASKS = {
|
||||
name="DishClassification",
|
||||
train_dir=os.path.join(settings.BASE_DIR, "dataset", "DishClassification", "train"),
|
||||
val_dir=os.path.join(settings.BASE_DIR, "dataset", "DishClassification", "val"),
|
||||
unknown_dir=os.path.join(settings.BASE_DIR, "dataset", "Unknown", "DishClassification","train"), # 未知类别食物目录
|
||||
embedding_dim=512,
|
||||
batch_size=16,
|
||||
lr=1e-3,
|
||||
@@ -60,7 +58,6 @@ TASKS = {
|
||||
name="WholeIngredientRecognition",
|
||||
train_dir=os.path.join(settings.BASE_DIR, "dataset", "WholeIngredientRecognition", "train"),
|
||||
val_dir=os.path.join(settings.BASE_DIR, "dataset", "WholeIngredientRecognition", "val"),
|
||||
unknown_dir=os.path.join(settings.BASE_DIR, "dataset", "Unknown","WholeIngredientRecognition", "train"), # 未知类别食物目录
|
||||
embedding_dim=512,
|
||||
batch_size=32,
|
||||
lr=8e-4,
|
||||
@@ -73,13 +70,14 @@ TASKS = {
|
||||
name="ProcessedIngredientRecognition",
|
||||
train_dir=os.path.join(settings.BASE_DIR, "dataset", "ProcessedIngredientRecognition", "train"),
|
||||
val_dir=os.path.join(settings.BASE_DIR, "dataset", "ProcessedIngredientRecognition", "val"),
|
||||
unknown_dir=os.path.join(settings.BASE_DIR, "dataset", "Unknown", "ProcessedIngredientRecognition","train"), # 未知类别食物目录
|
||||
embedding_dim=512,
|
||||
batch_size=16,
|
||||
lr=1e-3,
|
||||
triplet_margin=0.25,
|
||||
center_loss_weight=0.1,
|
||||
aug_strength="shape",
|
||||
# triplet_margin=0.25,
|
||||
triplet_margin=0.5,
|
||||
# center_loss_weight=0.5,
|
||||
center_loss_weight=20,
|
||||
aug_strength="medium",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -147,7 +145,7 @@ class TripletDataset(Dataset):
|
||||
每个样本包含:锚点(anchor)、正样本(positive)、负样本(negative)
|
||||
"""
|
||||
|
||||
def __init__(self, dataset_path: str, transform=None, samples_per_class: int = 100, unknown_dir: str = None, unknown_prob: float = 0.7):
|
||||
def __init__(self, dataset_path: str, transform=None, samples_per_class: int = 100):
|
||||
"""
|
||||
初始化三元组数据集
|
||||
|
||||
@@ -155,24 +153,18 @@ class TripletDataset(Dataset):
|
||||
dataset_path: 数据集路径
|
||||
transform: 数据变换
|
||||
samples_per_class: 每个类别最多使用的样本数
|
||||
unknown_dir: 未知类别数据目录
|
||||
unknown_prob: 使用未知类别作为负样本的概率
|
||||
"""
|
||||
self.dataset_path = dataset_path
|
||||
self.transform = transform
|
||||
self.samples_per_class = samples_per_class
|
||||
self.unknown_dir = unknown_dir
|
||||
self.unknown_prob = unknown_prob
|
||||
|
||||
# 加载数据集
|
||||
self.class_to_idx = {}
|
||||
self.idx_to_class = {}
|
||||
self.samples_by_class = defaultdict(list)
|
||||
self.all_samples = []
|
||||
self.unknown_samples = [] # 未知类别样本列表
|
||||
|
||||
self._load_dataset()
|
||||
self._load_unknown_samples()
|
||||
|
||||
def _load_dataset(self):
|
||||
"""加载数据集并按类别组织"""
|
||||
@@ -202,21 +194,6 @@ class TripletDataset(Dataset):
|
||||
for class_name, class_idx in self.class_to_idx.items():
|
||||
logger.info(f" {class_name}: {len(self.samples_by_class[class_idx])} 张图片")
|
||||
|
||||
def _load_unknown_samples(self):
|
||||
"""加载未知类别样本"""
|
||||
if not self.unknown_dir or not os.path.exists(self.unknown_dir):
|
||||
logger.info("未指定未知类别目录或目录不存在,跳过未知样本加载")
|
||||
return
|
||||
|
||||
# 遍历未知类别目录下的所有图片
|
||||
for root, dirs, files in os.walk(self.unknown_dir):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.png', '.jpg', '.jpeg')):
|
||||
img_path = os.path.join(root, file)
|
||||
self.unknown_samples.append(img_path)
|
||||
|
||||
logger.info(f"加载未知类别样本完成: {len(self.unknown_samples)} 张图片")
|
||||
|
||||
def __len__(self):
|
||||
return len(self.all_samples)
|
||||
|
||||
@@ -242,41 +219,16 @@ class TripletDataset(Dataset):
|
||||
positive_path = anchor_path # 如果只有一张图片,使用自己作为正样本
|
||||
positive_img = self._load_image(positive_path)
|
||||
|
||||
# 获取负样本(使用策略选择未知类别或已知类别)
|
||||
negative_img = self._get_negative_sample()
|
||||
|
||||
return anchor_img, positive_img, negative_img, anchor_label
|
||||
|
||||
def _get_negative_sample(self):
|
||||
"""
|
||||
获取负样本,支持未知类别和已知类别的混合策略
|
||||
|
||||
Returns:
|
||||
torch.Tensor: 负样本图片
|
||||
"""
|
||||
# 如果有未知类别样本且概率满足条件,优先选择未知类别
|
||||
if self.unknown_samples and random.random() < self.unknown_prob:
|
||||
# 选择未知类别作为负样本
|
||||
negative_path = random.choice(self.unknown_samples)
|
||||
return self._load_image(negative_path)
|
||||
|
||||
# 否则选择已知类别作为负样本(原来的逻辑)
|
||||
# 为了安全起见,选择一个随机锚点
|
||||
anchor_idx = random.randint(0, len(self.all_samples) - 1)
|
||||
anchor_path, anchor_label = self.all_samples[anchor_idx]
|
||||
|
||||
# 获取负样本(不同类别的图片)
|
||||
# 获得其它类别
|
||||
negative_classes = [cls for cls in self.samples_by_class.keys() if cls != anchor_label]
|
||||
if negative_classes:
|
||||
# 随便选一个类别
|
||||
negative_class = random.choice(negative_classes)
|
||||
# 随便选一个路径
|
||||
negative_path = random.choice(self.samples_by_class[negative_class])
|
||||
else:
|
||||
# 如果没有其他类别,使用自己作为负样本(这种情况应该很少见)
|
||||
negative_path = anchor_path
|
||||
# 随便选一个类别
|
||||
negative_class = random.choice(negative_classes)
|
||||
# 随便选一个路径
|
||||
negative_path = random.choice(self.samples_by_class[negative_class])
|
||||
negative_img = self._load_image(negative_path)
|
||||
|
||||
return self._load_image(negative_path)
|
||||
return anchor_img, positive_img, negative_img, anchor_label
|
||||
|
||||
def _load_image(self, image_path: str):
|
||||
"""加载并预处理图片"""
|
||||
@@ -694,17 +646,13 @@ def main(task_key: str = "dish"):
|
||||
train_dataset = TripletDataset(
|
||||
dataset_path=cfg.train_dir,
|
||||
transform=transform_train,
|
||||
samples_per_class=550,
|
||||
unknown_dir=cfg.unknown_dir,
|
||||
unknown_prob=0.7 # 70%概率使用未知类别作为负样本
|
||||
samples_per_class=550
|
||||
)
|
||||
|
||||
|
||||
val_dataset = TripletDataset(
|
||||
dataset_path=cfg.val_dir,
|
||||
transform=transform_val,
|
||||
samples_per_class=50,
|
||||
unknown_dir=cfg.unknown_dir,
|
||||
unknown_prob=0.7 # 70%概率使用未知类别作为负样本
|
||||
samples_per_class=50
|
||||
)
|
||||
|
||||
# 创建数据加载器
|
||||
@@ -872,5 +820,6 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("task", choices=list(TASKS.keys()), nargs="?", default="dish")
|
||||
args = parser.parse_args()
|
||||
main("whole_ingredient")
|
||||
main("processed_ingredient")
|
||||
# main("whole_ingredient")
|
||||
# main("dish")
|
||||
Reference in New Issue
Block a user