第2206篇:电商商品图片的AI理解——自动标签和属性提取的工程实现
2026/4/30大约 7 分钟
第2206篇:电商商品图片的AI理解——自动标签和属性提取的工程实现
适读人群:电商平台或商品管理系统的Java工程师 | 阅读时长:约15分钟 | 核心价值:商品图片自动打标签和属性提取的完整工程实现,大幅降低商品上架成本
电商平台有一个永恒的痛点:商品上架。
商家上传了图片,但商品标签、颜色、材质、适用场景这些属性,要么商家懒得填,要么填错,要么用词五花八门("天蓝色"、"蓝色"、"湖蓝"明明是差不多的颜色,搜索却不互通)。
这直接影响用户的搜索体验——搜"蓝色连衣裙"出来一堆不相关商品,或者好商品因为标签没打对而搜不到。
AI图片理解可以从根本上解决这个问题:不依赖商家填写的信息,直接从图片里提取标准化属性。
一、电商属性体系设计
不同品类的商品需要不同的属性体系,先设计数据模型:
/**
* 商品属性提取结果
*/
@Data
@Builder
public class ProductAttributes {
// 通用属性(所有品类都有)
private String category; // 品类(服装/数码/家居等)
private String subCategory; // 子品类
private List<String> colors; // 颜色列表(可能多色)
private String mainColor; // 主色
private List<String> tags; // 标签(用于搜索)
private String targetAudience; // 目标人群(男/女/儿童/通用)
private String style; // 风格(休闲/正式/运动/复古等)
private double qualityScore; // AI评估的商品图片质量分(0-1)
// 服装专属属性
private ClothingAttributes clothing;
// 数码产品专属属性
private ElectronicsAttributes electronics;
// 提取置信度
private Map<String, Double> fieldConfidence;
@Data
@Builder
public static class ClothingAttributes {
private String type; // 上衣/裤子/连衣裙/外套/内衣
private List<String> materials; // 面料(棉/丝/涤纶等)
private String pattern; // 图案(纯色/条纹/格纹/印花)
private String collar; // 领型(圆领/V领/翻领等)
private String sleeve; // 袖型(短袖/长袖/无袖等)
private String fit; // 版型(修身/宽松/直筒)
private List<String> occasions; // 适用场景
}
@Data
@Builder
public static class ElectronicsAttributes {
private String brand; // 品牌(如图片中可见)
private String condition; // 新旧程度(全新/二手等)
private List<String> ports; // 接口类型(如可见)
private String color; // 颜色
}
}二、商品属性提取服务
@Service
public class ProductAttributeExtractor {
private final VisionService visionService;
private final ObjectMapper objectMapper;
// 品类特定的属性提取提示词
private static final Map<String, String> CATEGORY_PROMPTS = new HashMap<>();
static {
CATEGORY_PROMPTS.put("clothing", """
这是一张服装商品图片。请提取以下属性(严格按JSON格式):
{
"category": "服装",
"subCategory": "上衣/裤子/连衣裙/外套/鞋类/配饰等",
"colors": ["颜色1", "颜色2"],
"mainColor": "主色调(标准颜色名称)",
"tags": ["标签1", "标签2", "标签3"],
"targetAudience": "男/女/儿童/中性",
"style": "休闲/正式/运动/复古/街头/优雅等",
"clothing": {
"type": "服装类型",
"materials": ["面料1"],
"pattern": "图案类型(纯色/条纹/格纹/印花/无规则)",
"collar": "领型(如适用)",
"sleeve": "袖型(如适用)",
"fit": "版型",
"occasions": ["适用场景1", "适用场景2"]
},
"qualityScore": 商品图片质量评分0到1,
"fieldConfidence": {
"colors": 置信度,
"materials": 置信度
}
}
注意:
- 颜色用标准名称,如"宝蓝色"不要写"很蓝的颜色"
- tags至少5个,要对搜索有帮助
- qualityScore:背景干净/光线好/多角度展示的图片得分高
只返回JSON。
""");
CATEGORY_PROMPTS.put("electronics", """
这是一张数码/电子产品图片。请提取属性(JSON格式):
{
"category": "数码",
"subCategory": "手机/平板/耳机/摄影/电脑等",
"colors": ["颜色"],
"mainColor": "主色",
"tags": ["标签列表"],
"targetAudience": "目标人群",
"electronics": {
"brand": "品牌(如图片中可见,否则null)",
"condition": "全新/二手/展示机等(如可判断)",
"color": "颜色",
"ports": ["可见的接口类型"]
},
"qualityScore": 质量评分,
"fieldConfidence": {}
}
只返回JSON。
""");
// 通用提示词(用于未知品类)
CATEGORY_PROMPTS.put("general", """
这是一张商品图片。请提取商品属性(JSON格式):
{
"category": "商品大类",
"subCategory": "子类别",
"colors": ["颜色列表"],
"mainColor": "主色调",
"tags": ["至少5个搜索标签"],
"targetAudience": "目标人群",
"style": "风格描述",
"qualityScore": 图片质量评分0到1,
"additionalAttributes": {
"其他重要属性": "值"
}
}
只返回JSON。
""");
}
/**
* 提取商品属性
*/
public ProductAttributes extract(byte[] productImage, String predictedCategory) {
// 选择合适的提示词
String category = predictedCategory != null ? predictedCategory.toLowerCase() : "general";
String prompt = CATEGORY_PROMPTS.getOrDefault(category,
CATEGORY_PROMPTS.get("general"));
VisionRequest request = VisionRequest.builder()
.images(List.of(ImageInput.fromBytes(productImage, "image/jpeg")))
.prompt(prompt)
.metadata(Map.of("detail", "high"))
.build();
String response = visionService.analyzeImage(request).getContent();
return parseAttributes(response);
}
/**
* 批量处理多张商品图片(适合商品有多角度图的情况)
*/
public ProductAttributes extractFromMultipleImages(List<byte[]> productImages,
String predictedCategory) {
if (productImages.isEmpty()) throw new IllegalArgumentException("图片列表不能为空");
if (productImages.size() == 1) return extract(productImages.get(0), predictedCategory);
// 最多使用前4张图(超过4张对提取效果提升有限但成本增加)
List<byte[]> selectedImages = productImages.subList(0,
Math.min(4, productImages.size()));
String category = predictedCategory != null ? predictedCategory.toLowerCase() : "general";
String basePrompt = CATEGORY_PROMPTS.getOrDefault(category,
CATEGORY_PROMPTS.get("general"));
String multiImagePrompt = "以下是同一商品的多角度图片(图片1是主图,其余是辅图)。\n"
+ "请综合所有图片提取最完整准确的属性。\n\n" + basePrompt;
List<ImageInput> imageInputs = selectedImages.stream()
.map(img -> ImageInput.fromBytes(img, "image/jpeg"))
.collect(Collectors.toList());
VisionRequest request = VisionRequest.builder()
.images(imageInputs)
.prompt(multiImagePrompt)
.metadata(Map.of("detail", "high"))
.build();
String response = visionService.analyzeImage(request).getContent();
return parseAttributes(response);
}
private ProductAttributes parseAttributes(String response) {
try {
String cleanJson = response.replaceAll("```json\\s*", "")
.replaceAll("```\\s*", "").trim();
JsonNode root = objectMapper.readTree(cleanJson);
ProductAttributes.ProductAttributesBuilder builder = ProductAttributes.builder()
.category(root.has("category") ? root.get("category").asText() : null)
.subCategory(root.has("subCategory") ? root.get("subCategory").asText() : null)
.mainColor(root.has("mainColor") ? root.get("mainColor").asText() : null)
.style(root.has("style") ? root.get("style").asText() : null)
.targetAudience(root.has("targetAudience") ? root.get("targetAudience").asText() : null)
.qualityScore(root.has("qualityScore") ? root.get("qualityScore").asDouble() : 0.5);
// 解析颜色列表
List<String> colors = new ArrayList<>();
if (root.has("colors")) root.get("colors").forEach(c -> colors.add(c.asText()));
builder.colors(colors);
// 解析标签
List<String> tags = new ArrayList<>();
if (root.has("tags")) root.get("tags").forEach(t -> tags.add(t.asText()));
builder.tags(tags);
return builder.build();
} catch (Exception e) {
log.error("商品属性解析失败: {}", response, e);
return ProductAttributes.builder()
.tags(List.of())
.colors(List.of())
.qualityScore(0)
.build();
}
}
}三、属性标准化与搜索优化
提取出来的属性还需要做标准化,才能发挥搜索的价值:
@Service
public class AttributeNormalizer {
// 颜色标准化映射
private static final Map<String, String> COLOR_NORMALIZATION = new HashMap<>();
static {
// 将多种颜色表达方式统一
COLOR_NORMALIZATION.put("天蓝", "蓝色");
COLOR_NORMALIZATION.put("湖蓝", "蓝色");
COLOR_NORMALIZATION.put("宝蓝", "深蓝色");
COLOR_NORMALIZATION.put("藏蓝", "深蓝色");
COLOR_NORMALIZATION.put("米色", "米白色");
COLOR_NORMALIZATION.put("米白", "米白色");
COLOR_NORMALIZATION.put("米黄", "米白色");
COLOR_NORMALIZATION.put("驼色", "棕色");
COLOR_NORMALIZATION.put("卡其色", "棕色");
// ... 更多映射
}
/**
* 颜色标准化
*/
public List<String> normalizeColors(List<String> rawColors) {
return rawColors.stream()
.map(color -> COLOR_NORMALIZATION.getOrDefault(color, color))
.distinct()
.collect(Collectors.toList());
}
/**
* 标签去重和权重排序
* 更具体的标签权重更高("真丝连衣裙"比"连衣裙"更有价值)
*/
public List<String> optimizeTags(List<String> rawTags, ProductAttributes attrs) {
Set<String> tagSet = new LinkedHashSet<>();
// 优先级1:品类标签
if (attrs.getSubCategory() != null) tagSet.add(attrs.getSubCategory());
if (attrs.getCategory() != null) tagSet.add(attrs.getCategory());
// 优先级2:颜色+品类组合标签
if (attrs.getMainColor() != null && attrs.getSubCategory() != null) {
tagSet.add(attrs.getMainColor() + attrs.getSubCategory());
}
// 优先级3:原始提取标签
tagSet.addAll(rawTags);
// 去掉过于通用的标签
Set<String> tooGeneric = Set.of("商品", "产品", "图片", "正品");
return tagSet.stream()
.filter(t -> !tooGeneric.contains(t))
.filter(t -> t.length() >= 2) // 去掉单字标签
.limit(20) // 最多20个标签
.collect(Collectors.toList());
}
}四、生产环境部署的成本控制
商品图片属性提取在电商场景下是高频操作,成本控制很重要:
@Service
public class ProductAttributeExtractionService {
private final ProductAttributeExtractor extractor;
private final AttributeNormalizer normalizer;
private final RedisTemplate<String, ProductAttributes> redisTemplate;
/**
* 带缓存的属性提取
* 相同图片的MD5相同时,复用缓存结果
*/
public ProductAttributes extractWithCache(byte[] imageBytes, String category) {
// 计算图片内容哈希
String imageHash = DigestUtils.md5DigestAsHex(imageBytes);
String cacheKey = "product:attr:" + imageHash;
// 先查缓存
ProductAttributes cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
// 缓存未命中,调用AI提取
ProductAttributes attrs = extractor.extract(imageBytes, category);
// 归一化处理
if (attrs.getColors() != null) {
attrs.setColors(normalizer.normalizeColors(attrs.getColors()));
}
if (attrs.getTags() != null) {
attrs.setTags(normalizer.optimizeTags(attrs.getTags(), attrs));
}
// 存入缓存(7天有效期,相同图片的属性基本不变)
redisTemplate.opsForValue().set(cacheKey, attrs, Duration.ofDays(7));
return attrs;
}
/**
* 商品图片质量过滤:只对高质量图片做详细提取
*/
public ProductAttributes extractWithQualityFilter(byte[] imageBytes, String category) {
// 先快速评估图片质量(用低清模式,成本低)
VisionRequest quickCheck = VisionRequest.builder()
.images(List.of(ImageInput.fromBytes(imageBytes, "image/jpeg")))
.prompt("这张图片是否适合作为商品主图?(1=适合,0=不适合,如模糊/水印过多/无商品),只回答数字。")
.metadata(Map.of("detail", "low"))
.build();
String quality = visionService.analyzeImage(quickCheck).getContent().trim();
if ("0".equals(quality)) {
// 图片质量差,只返回基本属性
return ProductAttributes.builder()
.qualityScore(0.1)
.tags(List.of("图片质量差"))
.colors(List.of())
.build();
}
// 图片质量OK,做完整提取
return extractWithCache(imageBytes, category);
}
}五、实际效果与ROI
在一个中型电商平台(日新增商品5万件)的实际数据:
| 指标 | 人工打标 | AI打标 |
|---|---|---|
| 单商品耗时 | 3-5分钟 | 约3秒 |
| 标签数量 | 平均5个 | 平均12个 |
| 颜色标准化 | 不一致 | 统一标准 |
| 每日处理量 | 约2000件(人力限制) | 5万件全量 |
| 搜索找回率 | 基线 | 提升23% |
AI打标的最大价值不只是省人力,更在于覆盖了原本人工来不及打标的商品,提升了整个平台的搜索质量。
