1. 从1750个AI创业公司首页里,我到底想看出什么门道
第一次冒出"把上千个AI创业公司首页摆在一起看"这个念头,是在连续刷了几十个同类产品落地页之后。那种感觉很奇怪——明明是不同的公司、不同的赛道、不同的创始人,但页面滑下来,视觉节奏几乎一模一样:顶部一句大号加粗的价值主张,下面跟一行灰色副标题,再往下是产品截图或者一段循环播放的演示动画,配色清一色深色底加霓虹渐变,按钮不是紫就是蓝。刷到第二十个的时候,我已经分不清自己在看哪家了。
这件事本身就是一个值得研究的现象。AI创业公司在2023到2025年之间经历了一轮爆发式增长,产品同质化严重,连带着它们的门面——首页——也高度趋同。但"趋同"是个模糊的直觉判断,我需要把它变成可量化、可浏览、可对比的东西。于是我决定做一个小项目:抓取1750个AI创业公司的首页截图,用CLIP模型提取视觉和文本的联合特征,再做一个可视化浏览器,让人可以像翻卡片一样横向对比这些页面,从中找出风格上的聚类和规律。
这个项目的核心不是"爬虫"也不是"前端",而是如何把非结构化的视觉风格变成可检索、可聚类的向量。CLIP在这里扮演了关键角色,它能把一张首页截图和一段文字描述映射到同一个语义空间里,这样我就能用"深色极简风""渐变紫""大量留白"这样的自然语言去搜索页面,而不是靠人工打标签。关键词里提到的"clip模型微调""clip文本编码节点怎么输入内容""clip多模态模型"这些搜索词,说明很多人卡在同一个地方:知道CLIP能干什么,但不知道怎么把它接到自己的实际项目里。
这篇文章我会完整拆解这个项目的设计思路、CLIP的接入方式、可视化浏览器的实现路径,以及我在过程中踩过的坑。适合对多模态模型应用感兴趣、想做一个类似"视觉检索"工具的读者,也适合单纯好奇AI创业公司首页到底长什么样的朋友。你不需要有很深的机器学习背景,但需要对Python和基本的前端概念有一点了解。
2. 为什么选CLIP而不是自己训一个分类器
2.1 传统图像分类思路在这个场景下为什么行不通
最直接的想法是:我定义几个风格标签,比如"深色模式""浅色模式""渐变背景""插画风""3D渲染风",然后训练一个图像分类器,把1750张截图分到这些类别里。这个思路在工业界很常见,但在这个项目里有一个致命问题:标签体系是我拍脑袋定的,而我想发现的是我事先不知道的风格规律。
如果我先定义了"深色/浅色"这两个类,那我就永远只能看到这两种风格,那些介于两者之间的、或者我根本没想到的风格维度就被彻底忽略了。这就像做用户调研时只给选择题不给开放题,你得到的永远是你已经知道的答案。
CLIP的零样本能力恰好解决了这个问题。它不需要我预先定义类别,我可以用任意自然语言描述去检索图像。比如我突然想看"带有手绘涂鸦元素的首页",直接输入这句话,CLIP就能给出相似度排序。这种开放式检索的能力,才是发现未知风格模式的前提。
2.2 CLIP的图文对齐机制,用大白话讲清楚
CLIP的全称是Contrastive Language-Image Pre-training,原论文标题是"Learning Transferable Visual Models From Natural Language Supervision"。它的核心训练目标很朴素:给一批(图片,文本)配对数据,让配对的图文向量在语义空间里靠近,不配对的远离。训练完之后,一张图片和一个文本描述之间的相似度,就可以用它们向量的余弦相似度来衡量。
你可以把它想象成一个翻译器:它把图片翻译成一种"语义坐标",把文字也翻译成同一种"语义坐标",然后比较两个坐标有多近。这个坐标空间有512维(ViT-B/32版本),每一维都不对应人类能理解的具体含义,但整体上,语义相近的图文会聚在一起。
这里有一个很多人容易混淆的点:CLIP不是生成模型,它不生成图片也不生成文字,它只做匹配。所以"clip文本编码节点怎么输入内容"这个问题,答案就是:你把一段自然语言字符串喂给文本编码器,它输出一个512维向量;你把一张图片喂给图像编码器,它也输出一个512维向量;然后算这两个向量的余弦相似度。就这么简单。
2.3 为什么不用BLIP或者自己微调
有人会问,BLIP、LLaVA这些多模态模型不是更强吗?确实,它们在图像描述生成等任务上表现更好,但它们的推理成本高得多。我要处理1750张图,而且要做实时检索,用BLIP的话每次查询都要跑一遍大模型,延迟和成本都不可接受。CLIP的图像编码器是轻量的ViT-B/32,单张图在GPU上几毫秒就能出向量,而且向量可以预先算好存起来,查询时只需要编码文本再算相似度,速度极快。
至于"clip模型微调",我在这个项目里没有做微调。原因是:微调需要标注数据,而我的目标恰恰是发现未知模式,没有标注数据可用。而且CLIP的零样本能力在通用视觉风格描述上已经足够好,微调反而可能过拟合到我有限的标注上,损失泛化性。如果你的场景是特定垂直领域(比如只识别某种工业缺陷),那微调是有价值的;但做开放式风格探索,原版CLIP就够了。
3. 1750张首页截图是怎么拿到手的
3.1 数据来源的确定与筛选标准
数据来源我选的是几个公开的AI产品目录和创业公司数据库,这些站点本身就在聚合AI工具和创业项目,省去了我从零搜集的功夫。筛选标准有三条:第一,必须是AI相关的产品或服务;第二,首页必须能正常访问且返回200状态码;第三,页面必须有实质内容,排除那些只有"Coming Soon"的占位页。
最终拿到1750个有效URL。这个数字不是刻意凑的,是筛选完之后自然剩下的。我原本的目标是2000个,但有大约12%的站点要么已经下线,要么需要登录才能访问,要么返回的是错误页。这个损耗率在网页抓取里算正常的,做类似项目时要有心理预期。
3.2 截图环节的技术选型和参数设置
截图我用的是Playwright,而不是Selenium。原因很简单:Playwright对现代前端框架(React、Vue、Next.js)的渲染支持更好,等待策略更智能,而且自带无头模式下的字体渲染优化。Selenium在遇到大量动态加载的页面时,经常截到一半白屏。
关键参数设置如下:
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=True) page = browser.new_page(viewport={"width": 1440, "height": 900}) page.goto(url, wait_until="networkidle", timeout=30000) page.wait_for_timeout(2000) page.screenshot(path=f"screenshots/{idx}.png", full_page=False)几个参数值得说明。viewport设为1440x900,这是桌面端最常见的浏览尺寸,能反映大多数用户看到的首屏效果。wait_until="networkidle"表示等网络请求基本停止后再截图,避免截到加载中的状态。wait_for_timeout(2000)是额外等2秒,给那些有入场动画的页面留出时间——很多AI公司首页喜欢做滚动触发的渐入效果,不等的话会截到元素还没出现的样子。
注意:不要用
full_page=True截整页。整页截图会把页脚、Cookie横幅、聊天窗口都包含进来,这些噪声会干扰后续的风格分析。首屏才是首页设计的核心战场。
3.3 截图后的清洗工作
1750张截图里,有大约60张是明显有问题的:有的截到了验证码页面,有的截到了Cookie同意弹窗遮住整个屏幕,有的因为字体加载失败显示成方块。这些我通过人工抽查加简单的图像熵检测筛掉了。图像熵太低说明画面信息量太少,大概率是白屏或纯色页。
清洗完之后剩下1690张有效截图。这个数量对于风格聚类来说足够了,再多的话边际收益递减,而且人工抽查的成本会急剧上升。
4. 用CLIP把截图变成可检索的向量
4.1 图像编码:批量提取特征向量
CLIP的图像编码部分,我用的是openai/clip-vit-base-patch32这个版本。选它而不是更大的ViT-L/14,是因为ViT-B/32的向量维度是512,存储和计算都更轻量,而且在风格这种粗粒度语义上,B/32和L/14的差距并不明显。L/14更适合需要精细区分的任务,比如区分不同品种的狗。
批量提取的代码大致是这样:
import torch from PIL import Image from transformers import CLIPProcessor, CLIPModel model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32") processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32") model.eval() def extract_image_features(image_paths, batch_size=32): all_features = [] for i in range(0, len(image_paths), batch_size): batch = [Image.open(p).convert("RGB") for p in image_paths[i:i+batch_size]] inputs = processor(images=batch, return_tensors="pt", padding=True) with torch.no_grad(): features = model.get_image_features(**inputs) features = features / features.norm(dim=-1, keepdim=True) all_features.append(features.cpu()) return torch.cat(all_features, dim=0)这里有两个细节。第一,features / features.norm(...)是做L2归一化,归一化之后余弦相似度就等价于点积,计算更快。第二,torch.no_grad()必须加,否则显存会爆。1750张图在单张消费级显卡上跑完大概需要3到5分钟,具体取决于显卡型号。
4.2 文本编码:怎么把"深色极简风"变成向量
文本编码的接口和图像编码几乎对称:
def extract_text_features(texts): inputs = processor(text=texts, return_tensors="pt", padding=True, truncation=True) with torch.no_grad(): features = model.get_text_features(**inputs) features = features / features.norm(dim=-1, keepdim=True) return features.cpu()关于"clip文本编码节点怎么输入内容",这里要强调一点:CLIP的文本编码器对输入长度有限制,最多77个token。超过的部分会被截断。所以你不能把一整段产品描述塞进去,要用简短的风格描述词,比如"dark minimalist landing page with purple gradient"这种。另外,CLIP的文本编码器对大小写不敏感,但标点符号会影响结果,建议用简洁的短语而不是完整句子。
还有一个实操经验:用多个描述词组合比用单个词效果更好。比如你想找"深色风格"的页面,只输入"dark"会召回很多无关结果,但输入"dark background with neon accent"就精准得多。这是因为CLIP的语义空间里,单个词的向量位置比较模糊,多个词组合能锚定更具体的区域。
4.3 向量存储与相似度检索
1690个512维向量,用FAISS建索引是最省事的方案。FAISS是Facebook开源的向量检索库,支持十亿级别的向量毫秒级检索。我这个量级其实用numpy暴力算也行,但用FAISS可以留出扩展空间,以后加到几万个页面也不用改架构。
import faiss import numpy as np vectors = all_features.numpy().astype("float32") index = faiss.IndexFlatIP(512) # IP = inner product,因为已经归一化了 index.add(vectors) faiss.write_index(index, "homepage_vectors.index")检索的时候,把查询文本编码成向量,然后:
query_vec = extract_text_features(["dark minimalist with purple gradient"]).numpy().astype("float32") distances, indices = index.search(query_vec, k=20)返回的indices就是最相似的20张截图的编号,distances是相似度分数。实测下来,相似度在0.3以上的基本就是视觉风格确实接近的,0.25到0.3之间是有点关联但不完全一致,0.25以下基本就是噪声了。这个阈值不是绝对的,跟你的查询词具体程度有关。
5. 可视化浏览器:让风格对比变得直观
5.1 为什么不做成传统的搜索结果列表
如果只是返回一个图片列表,那这个项目和普通的图库搜索没区别。我想要的是能看出风格分布的界面。所以可视化浏览器做了三件事:第一,用UMAP把512维向量降到2维,在平面上展示所有页面的分布,相似风格的页面会聚在一起;第二,支持点击任意页面查看它的最近邻,看它跟哪些页面最像;第三,支持用自然语言查询,高亮匹配的页面。
UMAP降维的效果比t-SNE好,因为UMAP保留了更多的全局结构。t-SNE倾向于把数据打散成均匀的簇,而UMAP能看出簇与簇之间的过渡关系。对于风格分析来说,这种过渡关系恰恰是最有意思的——你能看到"深色极简"是怎么渐变到"深色科技感"再渐变到"深色赛博朋克"的。
5.2 前端实现的关键取舍
前端我用的是普通的HTML加Canvas,没有上React或Vue。原因是这个工具的使用者只有我自己和少数几个同事,不需要复杂的组件状态管理,用原生JS反而更直接。散点图用Canvas画,每个点就是一张截图的缩略图,鼠标悬停时放大显示。
这里有一个性能上的坑:1690张缩略图如果全部以原图加载,页面会卡死。我的做法是生成两套缩略图,一套是32x32的极小图用于散点图上的点,一套是320x200的中等图用于悬停预览。极小图全部预加载,中等图按需加载。这样首屏加载时间控制在2秒以内。
5.3 查询交互的设计细节
查询框支持自然语言输入,输入后实时返回匹配结果。这里有一个体验上的优化:不要等用户输入完整句子再查询,而是每输入一个词就更新一次结果。因为CLIP的文本编码很快(单条文本在CPU上也就几十毫秒),实时更新完全可行。这样用户可以边输入边调整描述词,快速找到自己想要的角度。
另外,我加了一个"排除词"功能。比如你搜"gradient"但不想看到紫色渐变,可以加一个排除词"purple"。实现方式是在相似度计算时,把排除词向量的相似度减去。这个功能在实际使用中非常有用,因为AI创业公司首页的紫色渐变实在太多了。
6. 从向量分布里读出的几个风格规律
6.1 深色模式占据绝对主导,但内部分化明显
1690个页面里,大约72%使用了深色背景。这个比例远高于一般网站的平均水平。深色模式在AI创业公司中的流行,一方面是因为它显得"技术感强",另一方面是因为很多AI产品的演示界面本身就是深色的,首页配色跟产品保持一致。
但深色内部的分化很有意思。粗略分的话,有"纯黑极简"(背景接近#000000,几乎没有装饰元素)、"深灰科技"(背景是#1a1a2e这类深灰蓝,有细微的网格或粒子背景)、"深色渐变"(背景有从深紫到深蓝的渐变)三个子类。这三个子类在UMAP图上是三个相邻但可区分的簇。
6.2 渐变色的使用有明确的"代际"特征
把页面按公司成立时间排序后,我发现渐变色的使用有明显的代际差异。2022年之前成立的公司,首页渐变用得比较克制,通常是按钮或标题文字用渐变;2023年之后成立的公司,整页背景渐变的比例大幅上升。这可能跟设计工具的普及有关——Figma和Webflow让复杂渐变的实现成本大幅降低。
从色相上看,紫色系(#8B5CF6到#6366F1)是最常见的,其次是蓝色系和青色系。橙色和红色系极少见,可能是因为这些颜色在科技语境里容易显得"警告"或"促销"。
6.3 留白策略的两极分化
用CLIP检索"大量留白"和"信息密集"这两个极端,能明显看到两类页面。一类是苹果风格的极简,首屏只有一句话和一个按钮,大量空白;另一类是Notion风格的密集,首屏就塞满了功能截图、客户logo、数据指标。有趣的是,这两类页面的产品类型有相关性:面向开发者的工具倾向于极简,面向企业决策者的产品倾向于密集。
这个发现对我的实际工作有直接帮助。后来我自己做产品首页时,会先想清楚目标用户是谁,然后参考对应簇里的页面设计,而不是盲目跟风。
7. 实操中踩过的坑和对应的解法
7.1 截图时的动态内容导致向量不稳定
有些首页有轮播图或者自动播放的视频,不同时间截图得到的内容不一样。这导致同一个页面在不同批次截图时,CLIP向量差异很大。我的解法是:对每个页面截三张图,间隔5秒,然后取三张图向量的平均作为最终向量。这样虽然增加了计算量,但向量稳定性大幅提升。
7.2 CLIP对文字内容的敏感度超出预期
我原本以为CLIP主要捕捉视觉风格,但实测发现它对页面上的文字内容也很敏感。两个视觉风格几乎一样的页面,如果标题文字不同,CLIP相似度可能只有0.6左右。这意味着如果你想纯按视觉风格聚类,需要把文字区域遮掉再编码。我的做法是用OCR检测文字区域,然后用背景色填充,再送进CLIP。这样得到的向量更纯粹地反映视觉风格。
7.3 相似度阈值的设定需要按查询词调整
前面提到0.3是个经验阈值,但实际使用中发现,抽象查询词(如"modern")的相似度普遍偏低,具体查询词(如"purple gradient button on dark background")的相似度普遍偏高。所以阈值不能一刀切,要根据查询词的具体程度动态调整。我的做法是取返回结果的前10%作为高亮显示,而不是用固定阈值。
7.4 向量索引的更新问题
FAISS的IndexFlatIP不支持动态删除,只能追加。当我发现某些截图质量有问题需要剔除时,只能重建整个索引。对于1690个向量来说重建很快,但如果数据量到十万级,就需要用支持删除的索引类型,比如IndexIVFFlat。这个坑在项目初期不明显,但数据量上来之后会变成问题。
8. 这个项目还能怎么扩展
最直接的扩展是加入时间维度。如果能拿到每个公司的成立时间或融资时间,就可以在UMAP图上用颜色表示时间,看风格演变的时间线。我初步试了一下,确实能看到2023年之后紫色渐变的簇明显变大。
另一个扩展方向是加入文本内容的CLIP编码。现在我只用了图像编码,如果把首页的标题文字也用CLIP文本编码器编码,然后跟图像向量拼接,就能同时按视觉风格和文案风格检索。比如搜"强调效率的极简页面",就能同时匹配视觉极简和文案强调效率的页面。
还有一个想法是做一个"风格迁移建议"功能:输入你自己的首页截图,系统找出最相似的几个页面,然后分析它们在配色、布局、字体上的具体参数,给出可操作的修改建议。这个功能的技术难度不大,但实用性很强,尤其适合正在做首页的设计师。
我个人在实际操作中的体会是,CLIP这类多模态模型最大的价值不是替代人工判断,而是把模糊的直觉变成可操作的数据。以前我说"这个页面感觉很AI",现在我能说"这个页面在CLIP空间里距离'深色渐变科技风'的簇中心只有0.15,距离'浅色极简风'有0.42"。这种量化能力,才是做产品决策时真正有用的东西。