Việc thu thập hàng ngàn hình ảnh để đào tạo mô hình thị giác máy tính là một nhiệm vụ thường gặp phải hai vấn đề: bị chặn từ phía nguồn dữ liệu (Google Images, Pinterest, các trang thương mại điện tử, các trang web hình ảnh) và tiêu tốn băng thông một cách bất ngờ. Một IP nhanh chóng bị cấm, và hóa đơn cho băng thông proxy có thể vượt quá chi phí cho việc gán nhãn bộ dữ liệu. Trong bài viết này, chúng ta sẽ xem xét cách thiết kế một trình thu thập hình ảnh không bị mất dữ liệu do bị chặn và cách tính toán ngân sách cho băng thông trước.
Tại sao cần proxy khi thu thập hình ảnh cho CV
Các bộ dữ liệu cho thị giác máy tính hiếm khi được thu thập từ một nguồn duy nhất. Thông thường, đó là sự kết hợp giữa kết quả tìm kiếm hình ảnh, thẻ sản phẩm trên các trang thương mại điện tử, các trang web hình ảnh, mạng xã hội và API công khai. Hầu hết các nguồn này đều đặt ra giới hạn về số lượng yêu cầu từ một IP: Google Images hiển thị captcha sau vài trăm yêu cầu liên tiếp, Pinterest tạm thời chặn truy cập vào API, trong khi Wildberries và Ozon trả về thẻ rỗng hoặc chuyển hướng đến trang kiểm tra khi có hoạt động nghi ngờ.
Nếu không có proxy, bạn sẽ gặp phải giới hạn chỉ vài trăm hoặc hàng ngàn hình ảnh mỗi ngày từ một địa chỉ. Nếu cần một bộ dữ liệu từ 50.000 đến 500.000 hình ảnh (đó là số lượng thường cần cho các mạng nơ-ron tích chập và biến thể trong các bài toán phân loại và phát hiện), việc thu thập trong thời gian hợp lý là không thể nếu không có một nhóm IP phân tán — bạn sẽ liên tục gặp phải các chặn, hoặc nhận được một mẫu bị lệch do giới hạn về thời gian trong ngày và địa lý.
Lý do thứ hai là sự đa dạng địa lý của dữ liệu. Nếu mô hình phải nhận diện các đối tượng một cách đồng đều bất kể khu vực chụp (ví dụ, biển báo giao thông ở các quốc gia khác nhau hoặc sản phẩm trên các trang thương mại địa phương), trình thu thập phải kết nối với IP từ các quốc gia khác nhau. Một trung tâm dữ liệu với một IP đầu ra duy nhất sẽ không cung cấp được sự đa dạng đó.
Bao nhiêu băng thông thực sự cần cho bộ dữ liệu
Chi phí băng thông khi thu thập hình ảnh thường bị đánh giá thấp. Vấn đề không chỉ nằm ở kích thước của các hình ảnh — các trang HTML, gói JS, hình thu nhỏ, pixel theo dõi và CSS cũng được truyền qua proxy nếu trình thu thập không được cấu hình chính xác cho các URL cần thiết.
| Loại nội dung | Trọng lượng trung bình | Băng thông cho 10.000 cái |
|---|---|---|
| Hình thu nhỏ (150–300 px) | 15–40 KB | ~250 MB |
| Hình ảnh độ phân giải trung bình (800–1200 px) | 80–200 KB | ~1,4 GB |
| Hình ảnh độ phân giải cao (2000+ px) | 400 KB – 1,5 MB | ~9,5 GB |
| Trang HTML của thẻ sản phẩm (không có hình ảnh) | 150–400 KB | ~2,7 GB |
Trên thực tế, nếu bạn thu thập 100.000 hình ảnh độ phân giải trung bình cùng với các trang thẻ sản phẩm (để trích xuất gán nhãn — danh mục, hộp, thuộc tính), tổng chi phí băng thông thường nằm trong khoảng 25–45 GB. Nếu cần hình ảnh độ phân giải cao cho các nhiệm vụ phát hiện các đối tượng nhỏ — hãy tính toán khoảng 80–150 GB cho cùng 100.000 cái. Những con số này nên được tính toán khi chọn gói proxy trước, chứ không phải sau.
Datacenter, residential và mobile: nên chọn cái nào
Để thu thập hình ảnh, ba yếu tố quan trọng là: tốc độ, chi phí băng thông và khả năng chống chặn. Những yếu tố này khác nhau rất nhiều giữa các loại proxy.
| Tham số | Datacenter | Residential | Mobile |
|---|---|---|---|
| Tốc độ | Cao | Trung bình | Thấp-trung bình |
| Khả năng chống chặn | Thấp | Cao | Rất cao |
| Chi phí băng thông | Thấp | Trung bình-cao | Cao |
| Phù hợp cho | API công khai, các trang web không có bảo vệ nghiêm ngặt | Google Images, Pinterest, các trang thương mại điện tử | Mạng xã hội có bảo vệ chống bot nghiêm ngặt |
Đối với hầu hết các nhiệm vụ thu thập hình ảnh, proxy residential mang lại sự cân bằng tối ưu — IP trông giống như người dùng thông thường, do đó các hệ thống chống bot của các trang thương mại điện tử và công cụ tìm kiếm hình ảnh ít khi kích hoạt captcha. Nếu nguồn là một trang web hình ảnh công khai hoặc API không có giới hạn nghiêm ngặt, rẻ hơn khi sử dụng proxy từ trung tâm dữ liệu — chúng nhanh hơn và cho phép truyền tải khối lượng lớn băng thông với giá thấp hơn mỗi GB. Đối với việc thu thập hình ảnh từ các ứng dụng di động hoặc mạng xã hội với bảo vệ chống bot tiên tiến, proxy di động là lựa chọn tốt hơn — chúng sử dụng IP thực từ các nhà mạng và gần như không bao giờ bị chặn.
Trình thu thập cơ bản bằng requests với vòng lặp proxy
Để bắt đầu, một kịch bản đồng bộ đơn giản bằng requests với vòng lặp proxy cho mỗi yêu cầu hoặc qua một số lượng yêu cầu nhất định là đủ. Dưới đây là ví dụ về việc tải xuống hình ảnh từ danh sách URL với kiểm tra loại nội dung và giới hạn kích thước tệp, để không vô tình tải xuống một tệp nặng không đúng định dạng.
import requests
import itertools
import hashlib
import os
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
proxy_cycle = itertools.cycle(PROXIES)
HEADERS = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "image/avif,image/webp,image/*,*/*;q=0.8",
}
def download_image(url, save_dir="dataset", max_size_mb=10, timeout=15):
proxy = next(proxy_cycle)
proxies = {"http": proxy, "https": proxy}
try:
resp = requests.get(url, headers=HEADERS, proxies=proxies, timeout=timeout, stream=True)
resp.raise_for_status()
content_type = resp.headers.get("Content-Type", "")
if "image" not in content_type:
return None
content_length = int(resp.headers.get("Content-Length", 0))
if content_length > max_size_mb * 1024 * 1024:
return None
file_hash = hashlib.md5(url.encode()).hexdigest()
ext = content_type.split("/")[-1].split(";")[0]
filepath = os.path.join(save_dir, f"{file_hash}.{ext}")
os.makedirs(save_dir, exist_ok=True)
with open(filepath, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
f.write(chunk)
return filepath
except requests.exceptions.RequestException as e:
print(f"Lỗi khi {url} qua {proxy}: {e}")
return None
Điểm quan trọng là kiểm tra Content-Length trước khi tải xuống nội dung phản hồi. Điều này tiết kiệm băng thông bằng cách loại bỏ các tệp quá nặng ngay cả trước khi tải xuống hoàn toàn. Việc luân phiên proxy qua itertools.cycle phân bổ đồng đều tải giữa các IP, giảm nguy cơ bị cấm cho một địa chỉ cụ thể.
Thu thập bất đồng bộ qua aiohttp
Kịch bản đồng bộ bằng requests rất phù hợp cho vài ngàn hình ảnh, nhưng đối với các bộ dữ liệu từ 50.000 tệp trở lên, cần có tính bất đồng bộ — nếu không, việc thu thập sẽ kéo dài hàng ngày. aiohttp kết hợp với semaphore để giới hạn độ song song — là một lựa chọn hiệu quả cho hàng chục ngàn nhiệm vụ đồng thời mà không làm quá tải nhóm proxy.
import asyncio
import aiohttp
import itertools
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
proxy_cycle = itertools.cycle(PROXIES)
sem = asyncio.Semaphore(50) # giới hạn số yêu cầu đồng thời
async def fetch_image(session, url, idx):
proxy = next(proxy_cycle)
async with sem:
try:
async with session.get(url, proxy=proxy, timeout=15) as resp:
if resp.status != 200:
return
content_type = resp.headers.get("Content-Type", "")
if "image" not in content_type:
return
data = await resp.read()
with open(f"dataset/{idx}.jpg", "wb") as f:
f.write(data)
except Exception as e:
print(f"Lỗi {url}: {e}")
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_image(session, url, i) for i, url in enumerate(urls)]
await asyncio.gather(*tasks)
# asyncio.run(main(url_list))
Semaphore với 50 kết nối đồng thời là điểm khởi đầu. Nếu các proxy residential chịu được tải mà không gia tăng lỗi 429 và timeout, có thể từ từ tăng lên 100–150. Điều quan trọng là theo dõi tỷ lệ yêu cầu không thành công: nếu nó tăng lên trên 5–7%, nên giảm độ song song thay vì tăng nhóm proxy vô hạn.
Scrapy + middleware proxy cho việc thu thập quy mô lớn
Để thu thập bộ dữ liệu trong môi trường sản xuất với hàng đợi tích hợp, thử lại và loại bỏ URL trùng lặp, sử dụng Scrapy với ImagesPipeline là tiện lợi hơn. Nó tự động tải xuống hình ảnh, kiểm tra tính hợp lệ của chúng qua PIL và có thể tạo ra các hình thu nhỏ với kích thước khác nhau.
# settings.py
ITEM_PIPELINES = {
'scrapy.pipelines.images.ImagesPipeline': 1,
}
IMAGES_STORE = 'dataset_images'
IMAGES_MIN_HEIGHT = 200
IMAGES_MIN_WIDTH = 200
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.ProxyRotationMiddleware': 350,
}
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 0.5
RETRY_TIMES = 3
# middlewares.py
import random
class ProxyRotationMiddleware:
PROXIES = [
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
"http://user:[email protected]:10000",
]
def process_request(self, request, spider):
request.meta['proxy'] = random.choice(self.PROXIES)
Các tham số IMAGES_MIN_HEIGHT và IMAGES_MIN_WIDTH tự động loại bỏ các biểu tượng nhỏ và sprite, làm ô nhiễm bộ dữ liệu và lãng phí băng thông. DOWNLOAD_DELAY 0.5 giây giảm tần suất yêu cầu từ một proxy, điều này giảm tỷ lệ captcha trên các nguồn có giám sát chống gian lận nghiêm ngặt như Pinterest.
Cách giảm tiêu thụ băng thông
Băng thông qua proxy thường được tính phí theo khối lượng, vì vậy bất kỳ tối ưu hóa nào cũng sẽ tiết kiệm ngân sách. Dưới đây là những mẹo hiệu quả:
- Yêu cầu hình thu nhỏ, không phải bản gốc, nếu độ phân giải 512–1024 px là đủ cho việc đào tạo — hầu hết các nguồn cung cấp hình ảnh với kích thước cần thiết qua một URL riêng hoặc tham số truy vấn.
- Lọc theo Content-Length trước khi tải xuống nội dung — yêu cầu HEAD hoặc đọc tiêu đề từ GET streaming cho phép loại bỏ các tệp quá nặng mà không tốn băng thông để tải xuống.
- Tắt tải xuống các tài nguyên đi kèm, nếu trình thu thập hoạt động qua trình duyệt headless: chặn CSS, phông chữ và script theo dõi, chỉ để lại HTML và các hình ảnh cần thiết.
- Loại bỏ URL trùng lặp trước khi yêu cầu, thay vì sau khi tải xuống — các hình ảnh giống nhau trên các trang thẻ sản phẩm (ví dụ, trên Wildberries) không cần tải xuống lại.
- Sử dụng bộ nhớ cache cục bộ cho các phản hồi trong các lần chạy lại trình thu thập trong quá trình gỡ lỗi, để không tốn băng thông qua proxy cho mỗi lần chạy thử nghiệm mã.
Các lỗi thường gặp, 403 và giới hạn tần suất
Khi thu thập hình ảnh với khối lượng lớn, trình thu thập chắc chắn sẽ gặp phải các phản hồi 403, 429 và chuyển hướng tạm thời đến các trang kiểm tra. Việc xử lý đúng các mã này tiết kiệm cả băng thông và nhóm proxy.
| Mã/symptom | Nguyên nhân | Cần làm gì |
|---|---|---|
| 403 Forbidden | IP bị liệt vào danh sách đen của nguồn | Luân phiên proxy, thay đổi User-Agent |
| 429 Too Many Requests | Vượt quá giới hạn yêu cầu mỗi phút | Tăng DOWNLOAD_DELAY, giảm độ song song |
| Chuyển hướng đến captcha | Phân tích hành vi nghi ngờ bot | Chuyển sang IP residential/mobile |
| Content-Length trống | Phản hồi chunked hoặc placeholder thay vì hình ảnh | Kiểm tra kích thước thực tế của các byte đã tải xuống |
Cần ghi lại tỷ lệ yêu cầu thành công cho từng proxy. Nếu một IP cụ thể liên tục cho 403 nhiều hơn các IP khác — hãy tự động loại bỏ nó khỏi vòng lặp, không chờ đợi cho đến khi nó kéo theo thống kê chung của toàn bộ nhóm.
Tính toán ngân sách cho băng thông proxy
Trước khi bắt đầu thu thập, hữu ích khi ước lượng khối lượng băng thông trước, chứ không phải tính toán sau. Công thức đơn giản: số lượng hình ảnh × trọng lượng trung bình của tệp × hệ số chi phí phát sinh (HTML, tiêu đề, yêu cầu lặp lại do lỗi — thường là 1.3–1.6).
Ví dụ tính toán cho một bộ dữ liệu 200.000 hình ảnh độ phân giải trung bình (~150 KB) với việc phân tích thẻ sản phẩm: 200.000 × 150 KB = 30 GB trọng lượng hình ảnh thuần. Với các trang HTML và yêu cầu lặp lại do các chặn tạm thời (hệ số 1.5), tổng chi phí sẽ là khoảng 45 GB. Nếu một số nguồn yêu cầu IP di động do bảo vệ chống bot, hãy tính toán điều này vào nhóm proxy riêng, vì băng thông trên IP di động thường tốn kém hơn so với residential hoặc datacenter.
Một thực tiễn tốt là thu thập một lô thử nghiệm từ 1.000–2.000 hình ảnh, đo lường tiêu thụ băng thông thực tế và tỷ lệ lỗi, sau đó ngoại suy cho toàn bộ khối lượng dự kiến của bộ dữ liệu. Điều này chính xác hơn bất kỳ tính toán lý thuyết nào, vì nó xem xét đặc thù của nguồn cụ thể.
Kết luận
Việc thu thập hình ảnh để đào tạo các mô hình thị giác máy tính là một nhiệm vụ giao thoa giữa kỹ thuật dữ liệu và quản lý hạ tầng mạng một cách hợp lý. Kiến trúc đúng đắn của trình thu thập (tính bất đồng bộ, lọc theo Content-Length, loại bỏ URL trùng lặp) tiết kiệm băng thông, trong khi việc lựa chọn proxy hợp lý quyết định xem bạn có thu thập được khối lượng bộ dữ liệu cần thiết trong vài ngày hay trong nhiều tuần với các chặn liên tục.
Nếu các nguồn dữ liệu sử dụng bảo vệ chống bot tiên tiến — các công cụ tìm kiếm hình ảnh, các trang thương mại điện tử, mạng xã hội — hãy bắt đầu với proxy residential: chúng cung cấp sự cân bằng tốt nhất giữa khả năng chống chặn và chi phí băng thông. Đối với các API công khai và các trang web không có giới hạn nghiêm ngặt, có thể tiết kiệm bằng cách sử dụng proxy từ trung tâm dữ liệu, và cho các ứng dụng di động được bảo vệ nhất — hãy kết nối với proxy di động cho phần thu thập khó khăn nhất.