Trong quá trình làm việc với Excel, hàm VLOOKUP là một công cụ quen thuộc nhưng cũng đầy thách thức. Nó giúp đối chiếu dữ liệu nhanh chóng giữa hai bảng, nhưng khi lượng dữ liệu lớn, cấu trúc bảng phức tạp hoặc cần cập nhật thường xuyên, thao tác thủ công trở nên rắc rối. Đặc biệt, khi dữ liệu không nằm trong Excel mà lại dưới dạng ảnh chụp màn hình, PDF hoặc bảng trong hình ảnh, việc xử lý càng trở nên khó khăn hơn.
Gần đây, khi xử lý một lô báo giá từ nhà cung cấp, tôi đã gặp phải vấn đề tương tự. Báo giá từ hàng chục nhà cung cấp có định dạng đa dạng: có bản là Excel, có bản là PDF, và có cả ảnh chụp màn hình. Tôi cần tổng hợp các báo giá này vào một bảng duy nhất, đối chiếu theo mã sản phẩm. Phương pháp truyền thống hoặc là nhập liệu thủ công, hoặc là viết script Python để phân tích. Cách thứ nhất tốn thời gian, cách thứ hai lại yêu cầu kỹ năng cao.
Sau đó, tôi phát hiện ra rằng mô hình ngôn ngữ lớn đa phương thức Qwen2.5-VL-7B-Instruct có thể giải quyết hiệu quả vấn đề này. Nó không chỉ hiểu được bảng biểu trong ảnh mà còn nắm bắt cấu trúc bảng, trích xuất dữ liệu và thực hiện đối chiếu theo yêu cầu của chúng ta. Bài viết này sẽ chia sẻ cách triển khai chức năng đối chiếu dữ liệu giữa các bảng tương tự VLOOKUP bằng mô hình này.
Tại sao chọn Qwen2.5-VL cho việc đối chiếu dữ liệu?
Bạn có thể tự hỏi, tại sao không sử dụng pandas trong Python để đối chiếu dữ liệu? Tại sao lại cần đến mô hình đa phương thức? Dưới đây là một số lý do chính:
- Nguồn dữ liệu đa dạng: Hơn một nửa báo giá từ nhà cung cấp tôi nhận được không phải ở định dạng Excel chuẩn. Có bản là PDF scan, có bản là ảnh chụp bằng điện thoại, hoặc ảnh chụp màn hình web. Việc xử lý dữ liệu phi cấu trúc này bằng các phương pháp truyền thống rất khó khăn.
- Cấu trúc bảng khác biệt: Tiêu đề bảng có thể ở dòng đầu tiên hoặc dòng thứ hai; có bảng dùng ô gộp, có bảng dùng màu sắc để đánh dấu; mã sản phẩm có thể ở cột A hoặc cột B. Việc sử dụng quy tắc cố định để phân tích dễ gây sai sót.
Qwen2.5-VL-7B-Instruct có ưu thế tự nhiên trong những trường hợp này. Nó được huấn luyện chuyên sâu về khả năng hiểu tài liệu và phân tích bảng biểu, có thể đọc hiểu các định dạng bảng khác nhau và hiểu cấu trúc logic của bảng. Ví dụ, nó có thể nhận diện đâu là tiêu đề bảng, đâu là dòng dữ liệu, và đâu là dòng tổng kết.
Quan trọng hơn, mô hình này hỗ trợ xuất dữ liệu có cấu trúc. Nó có thể trích xuất dữ liệu từ ảnh và xuất trực tiếp dưới dạng JSON, giúp chúng ta dễ dàng chuyển đổi sang cấu trúc dữ liệu Python để xử lý tiếp theo.
Chuẩn bị: Triển khai và Sử dụng Cơ bản
Triển khai nhanh Qwen2.5-VL
Nếu bạn chưa triển khai Qwen2.5-VL, đây là một cách đơn giản. Tôi sử dụng Ollama, giúp việc triển khai mô hình lớn trên máy cục bộ trở nên rất dễ dàng.
# Cài đặt Ollama (nếu chưa có)
# Truy cập https://ollama.com để tải phiên bản phù hợp
# Tải mô hình Qwen2.5-VL-7B
ollama pull qwen2.5vl:7b
# Chạy mô hình
ollama run qwen2.5vl:7b
Vậy là xong. Bây giờ bạn có thể trò chuyện với mô hình trên dòng lệnh. Tuy nhiên, chúng ta cần gọi chương trình, vì vậy cần có client Python.
# Cài đặt Python client
pip install ollama
Kiểm tra lần đầu: Để mô hình hiểu bảng
Trước khi bắt đầu đối chiếu dữ liệu phức tạp, hãy làm một bài kiểm tra đơn giản để xem mô hình có nhận diện chính xác nội dung bảng hay không.
Tôi đã chuẩn bị một ảnh bảng giá sản phẩm đơn giản với nội dung như sau:
Mã sản phẩm Đơn giá Tồn kho
A001 ¥120 50
A002 ¥150 30
A003 ¥200 20
Sử dụng Python để gọi mô hình nhận diện:
import ollama
import base64
def image_to_base64(image_path):
"""Chuyển đổi ảnh sang mã hóa base64"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
# Đọc và mã hóa ảnh
image_path = "product_price_table.png"
image_base64 = image_to_base64(image_path)
# Xây dựng tin nhắn
messages = [
{
"role": "user",
"content": "Vui lòng nhận diện nội dung bảng trong ảnh và xuất ra định dạng JSON, bao gồm các trường Mã sản phẩm, Đơn giá, Tồn kho.",
"images": [image_base64]
}
]
# Gọi mô hình
response = ollama.chat(
model='qwen2.5vl:7b',
messages=messages
)
print(response['message']['content'])
Sau khi chạy, mô hình sẽ xuất ra JSON tương tự như sau:
{
"dữ_liệu_bảng": [
{
"Mã sản phẩm": "A001",
"Đơn giá": "¥120",
"Tồn kho": "50"
},
{
"Mã sản phẩm": "A002",
"Đơn giá": "¥150",
"Tồn kho": "30"
},
{
"Mã sản phẩm": "A003",
"Đơn giá": "¥200",
"Tồn kho": "20"
}
]
}
Với kết quả này, bạn có thể tự tin rằng mô hình không chỉ hiểu bảng mà còn có thể xuất dữ liệu theo định dạng yêu cầu. Đây là nền tảng để chúng ta triển khai chức năng VLOOKUP.
Ý tưởng cốt lõi để triển khai đối chiếu dữ liệu giữa các bảng
VLOOKUP truyền thống hoạt động trong Excel, sử dụng một giá trị từ bảng này để tìm kiếm thông tin tương ứng trong bảng kia. Để triển khai chức năng tương tự bằng Qwen2.5-VL, chúng ta cần giải quyết một số vấn đề quan trọng:
Trích xuất dữ liệu: Lấy dữ liệu có cấu trúc từ nhiều định dạng khác nhau
Đầu tiên, bất kể nguồn dữ liệu là gì (ảnh, ảnh chụp PDF, ảnh Excel), chúng ta cần để mô hình trích xuất dữ liệu có cấu trúc.
Đây là một mẹo: Cung cấp cho mô hình hướng dẫn rõ ràng, chỉ cho nó biết định dạng dữ liệu chúng ta muốn:
import ollama
import base64
import json
def image_to_base64(image_path):
"""Chuyển đổi ảnh sang mã hóa base64"""
with open(image_path, "rb") as image_file:
return base64.b64encode(image_file.read()).decode('utf-8')
def extract_table_from_image(image_path, table_description):
"""Trích xuất dữ liệu bảng từ ảnh"""
image_base64 = image_to_base64(image_path)
prompt = f"""
Vui lòng nhận diện cẩn thận bảng trong ảnh.
Mô tả bảng: {table_description}
Vui lòng xuất theo các yêu cầu sau:
1. Nhận diện tất cả các dòng dữ liệu.
2. Xuất dưới dạng mảng JSON.
3. Mỗi đối tượng bao gồm tất cả các cột của bảng.
4. Giữ nguyên định dạng gốc của dữ liệu.
Nếu một số ô là ô gộp, hãy xử lý theo nội dung thực tế.
"""
messages = [
{
"role": "user",
"content": prompt,
"images": [image_base64]
}
]
response = ollama.chat(
model='qwen2.5vl:7b',
messages=messages
)
# Phân tích phản hồi JSON
try:
# Trích xuất phần JSON từ phản hồi
content = response['message']['content']
# Đôi khi mô hình thêm văn bản mô tả xung quanh JSON
if '```json' in content:
json_str = content.split('```json')[1].split('```')[0].strip()
elif '```' in content:
json_str = content.split('```')[1].strip()
else:
json_str = content
table_data = json.loads(json_str)
# Đảm bảo dữ liệu trả về là một danh sách (mảng)
if isinstance(table_data, dict) and "dữ_liệu_bảng" in table_data:
return table_data["dữ_liệu_bảng"]
elif isinstance(table_data, list):
return table_data
else:
print(f"Cấu trúc dữ liệu không mong muốn: {table_data}")
return None
except json.JSONDecodeError as e:
print(f"Lỗi phân tích JSON: {e}")
print(f"Phản hồi gốc: {content}")
return None
except Exception as e:
print(f"Lỗi xử lý phản hồi: {e}")
print(f"Phản hồi gốc: {content}")
return None
Đối chiếu dữ liệu: Thực hiện chức năng tìm kiếm tương tự VLOOKUP
Sau khi có dữ liệu có cấu trúc, bước tiếp theo là đối chiếu. Giả sử chúng ta có hai bảng:
- Bảng chính: Chứa mã sản phẩm, cần tìm giá tương ứng.
- Bảng tham chiếu: Chứa mã sản phẩm và thông tin giá.
Chúng ta cần sử dụng mã sản phẩm từ bảng chính để tìm kiếm giá trong bảng tham chiếu.
def vlookup_style_match(main_table, reference_table, lookup_key, return_key):
"""
Thực hiện chức năng đối chiếu tương tự VLOOKUP
Tham số:
- main_table: Dữ liệu bảng chính (danh sách các dictionary)
- reference_table: Dữ liệu bảng tham chiếu (danh sách các dictionary)
- lookup_key: Khóa dùng để tìm kiếm (ví dụ: 'Mã sản phẩm')
- return_key: Khóa để trả về giá trị (ví dụ: 'Đơn giá')
Trả về:
- Dữ liệu bảng chính sau khi đã đối chiếu
"""
# Xây dựng dictionary tra cứu từ bảng tham chiếu
# Sử dụng lookup_key làm khóa, và return_key làm giá trị tương ứng
lookup_dict = {}
for item in reference_table:
key_value = item.get(lookup_key)
if key_value:
# Làm sạch giá trị khóa, loại bỏ khoảng trắng có thể có
clean_key = str(key_value).strip()
lookup_dict[clean_key] = item.get(return_key)
# Thực hiện đối chiếu trong bảng chính
matched_table = []
for item in main_table:
matched_item = item.copy()
# Lấy giá trị cần tìm và làm sạch
lookup_value = str(item.get(lookup_key, '')).strip()
if lookup_value in lookup_dict:
matched_item[return_key] = lookup_dict[lookup_value]
matched_item['Trạng thái đối chiếu'] = 'Thành công'
else:
matched_item[return_key] = None
matched_item['Trạng thái đối chiếu'] = 'Không tìm thấy'
matched_table.append(matched_item)
return matched_table
Xử lý các vấn đề thường gặp khi đối chiếu
Trong thực tế, chúng ta sẽ gặp nhiều vấn đề đối chiếu khác nhau:
- Giá trị khóa không khớp hoàn toàn: Ví dụ, bảng chính là "A001", bảng tham chiếu là "A001 ".
- Định dạng khác nhau: Bảng chính là số 123, bảng tham chiếu là chuỗi "123".
- Đối chiếu một phần: Các trường hợp cần đối chiếu mờ.
Để giải quyết các vấn đề này, chúng ta có thể thêm một số logic xử lý:
def enhanced_vlookup(main_table, reference_table, lookup_key, return_key, fuzzy_match=False):
"""
Phiên bản VLOOKUP nâng cao, hỗ trợ đối chiếu mờ.
Tham số:
- main_table: Dữ liệu bảng chính.
- reference_table: Dữ liệu bảng tham chiếu.
- lookup_key: Khóa tìm kiếm.
- return_key: Khóa trả về.
- fuzzy_match: Cờ cho phép đối chiếu mờ.
Trả về:
- Dữ liệu bảng chính đã đối chiếu.
"""
# Tiền xử lý bảng tham chiếu, xây dựng các dạng khóa khác nhau
lookup_dict = {}
fuzzy_dict = {} # Dùng cho đối chiếu mờ
for item in reference_table:
key_value = item.get(lookup_key)
if not key_value:
continue
# Khóa cơ bản (làm sạch khoảng trắng)
clean_key = str(key_value).strip()
lookup_dict[clean_key] = item.get(return_key)
# Chuẩn bị cho đối chiếu mờ
if fuzzy_match:
# Chuyển về chữ thường, loại bỏ ký tự đặc biệt
fuzzy_key = clean_key.lower().replace('-', '').replace('_', '').replace(' ', '')
fuzzy_dict[fuzzy_key] = {
'original_key': clean_key,
'value': item.get(return_key)
}
# Đối chiếu bảng chính
matched_table = []
for item in main_table:
matched_item = item.copy()
original_value = item.get(lookup_key, '')
clean_value = str(original_value).strip()
# Đối chiếu chính xác
if clean_value in lookup_dict:
matched_item[return_key] = lookup_dict[clean_value]
matched_item['Trạng thái đối chiếu'] = 'Đối chiếu chính xác'
matched_item['Ghi chú đối chiếu'] = f'Tìm thấy chính xác: {clean_value}'
# Đối chiếu mờ (nếu được bật)
elif fuzzy_match:
fuzzy_value = clean_value.lower().replace('-', '').replace('_', '').replace(' ', '')
# Thực hiện đối chiếu đơn giản: kiểm tra sự bao hàm
matched = False
for fuzzy_key, data in fuzzy_dict.items():
if fuzzy_value in fuzzy_key or fuzzy_key in fuzzy_value:
matched_item[return_key] = data['value']
matched_item['Trạng thái đối chiếu'] = 'Đối chiếu mờ'
matched_item['Ghi chú đối chiếu'] = f'Tìm thấy mờ: {data["original_key"]}'
matched = True
break
if not matched:
matched_item[return_key] = None
matched_item['Trạng thái đối chiếu'] = 'Không tìm thấy'
matched_item['Ghi chú đối chiếu'] = f'Không tìm thấy: {clean_value}'
else:
matched_item[return_key] = None
matched_item['Trạng thái đối chiếu'] = 'Không tìm thấy'
matched_item['Ghi chú đối chiếu'] = f'Không tìm thấy: {clean_value}'
matched_table.append(matched_item)
return matched_table
Kịch bản thực tế: So sánh báo giá nhà cung cấp
Hãy dùng một ví dụ thực tế để minh họa toàn bộ quy trình. Giả sử bạn là quản lý mua hàng, cần so sánh báo giá từ ba nhà cung cấp.
Chuẩn bị dữ liệu
Bạn có ba nguồn dữ liệu:
- Danh sách yêu cầu (Ảnh chụp Excel): Bao gồm mã sản phẩm và số lượng cần mua.
- Báo giá Nhà cung cấp A (Ảnh chụp PDF): Bao gồm mã sản phẩm và đơn giá.
- Báo giá Nhà cung cấp B (Bảng trong ảnh): Bao gồm mã sản phẩm, đơn giá và thời gian giao hàng.
Đầu tiên, chúng ta trích xuất dữ liệu từ các nguồn này:
# Giả định các hàm image_to_base64 và extract_table_from_image đã được định nghĩa ở trên
# Trích xuất danh sách yêu cầu
demand_table = extract_table_from_image(
"demand_list.png",
"Danh sách yêu cầu mua hàng, bao gồm hai cột Mã sản phẩm và Số lượng mua"
)
# Trích xuất báo giá Nhà cung cấp A
supplier_a_table = extract_table_from_image(
"supplier_a_quote.png",
"Báo giá Nhà cung cấp A, bao gồm Mã sản phẩm, Đơn giá, Ghi chú, v.v."
)
# Trích xuất báo giá Nhà cung cấp B
supplier_b_table = extract_table_from_image(
"supplier_b_quote.jpg",
"Báo giá Nhà cung cấp B, bao gồm Mã sản phẩm, Giá, Thời gian giao hàng, v.v."
)
# In ra một vài dòng đầu để kiểm tra
if demand_table:
print("Dữ liệu yêu cầu:", json.dumps(demand_table[:3], indent=2, ensure_ascii=False))
else:
print("Không trích xuất được dữ liệu yêu cầu.")
if supplier_a_table:
print("\nBáo giá Nhà cung cấp A:", json.dumps(supplier_a_table[:3], indent=2, ensure_ascii=False))
else:
print("Không trích xuất được báo giá Nhà cung cấp A.")
if supplier_b_table:
print("\nBáo giá Nhà cung cấp B:", json.dumps(supplier_b_table[:3], indent=2, ensure_ascii=False))
else:
print("Không trích xuất được báo giá Nhà cung cấp B.")
Đối chiếu và so sánh dữ liệu
Bây giờ, chúng ta sử dụng mã sản phẩm từ danh sách yêu cầu để tìm kiếm giá từ hai nhà cung cấp và tiến hành so sánh.
def compare_supplier_quotes(demand_table, supplier_tables, supplier_names):
"""
So sánh báo giá từ nhiều nhà cung cấp.
Tham số:
- demand_table: Bảng yêu cầu.
- supplier_tables: Danh sách các bảng báo giá của nhà cung cấp.
- supplier_names: Danh sách tên nhà cung cấp.
Trả về:
- Bảng kết quả so sánh.
"""
# Khởi tạo bảng so sánh
comparison_table = []
if not demand_table:
return []
for demand_item in demand_table:
product_model = demand_item.get('Mã sản phẩm', '')
# Cố gắng chuyển đổi số lượng sang số, xử lý lỗi nếu không được
try:
quantity_str = demand_item.get('Số lượng', demand_item.get('Số lượng mua', '0'))
quantity = float(quantity_str) if quantity_str else 0
except ValueError:
quantity = 0 # Mặc định là 0 nếu không chuyển đổi được
comparison_item = {
'Mã sản phẩm': product_model,
'Số lượng yêu cầu': quantity
}
# Thực hiện đối chiếu với từng nhà cung cấp
for i, supplier_table in enumerate(supplier_tables):
if not supplier_table: # Bỏ qua nếu bảng nhà cung cấp rỗng
continue
supplier_name = supplier_names[i]
# Tìm sản phẩm trong báo giá của nhà cung cấp này
matched = False
for supplier_item in supplier_table:
# Cố gắng lấy mã sản phẩm từ các khóa có thể có
supplier_model = supplier_item.get('Mã sản phẩm') or supplier_item.get('Mã sản phẩm (SKU)') or supplier_item.get('Mã SKU')
if supplier_model and str(supplier_model).strip() == str(product_model).strip():
# Cố gắng lấy đơn giá và thời gian giao hàng
unit_price_raw = supplier_item.get('Đơn giá') or supplier_item.get('Giá')
delivery_time = supplier_item.get('Thời gian giao hàng') or supplier_item.get('Giao hàng dự kiến')
# Làm sạch đơn giá
unit_price = None
if unit_price_raw:
try:
# Loại bỏ ký hiệu tiền tệ và dấu phẩy
price_str = str(unit_price_raw).replace('¥', '').replace('¥', '').replace('$', '').replace(',', '').strip()
unit_price = float(price_str)
except ValueError:
unit_price = None # Đặt là None nếu không chuyển đổi được
# Lưu giá trị đã xử lý
comparison_item[f'{supplier_name}_Đơn giá'] = unit_price_raw # Lưu cả giá trị gốc để hiển thị
comparison_item[f'{supplier_name}_Thời gian giao hàng'] = delivery_time
# Tính tổng giá
if unit_price is not None and quantity is not None and quantity > 0:
total = unit_price * quantity
comparison_item[f'{supplier_name}_Tổng giá'] = round(total, 2)
else:
comparison_item[f'{supplier_name}_Tổng giá'] = 'Không tính được'
matched = True
break # Tìm thấy sản phẩm, chuyển sang nhà cung cấp tiếp theo
# Nếu không tìm thấy sản phẩm
if not matched:
comparison_item[f'{supplier_name}_Đơn giá'] = 'Không báo giá'
comparison_item[f'{supplier_name}_Thời gian giao hàng'] = 'Không báo giá'
comparison_item[f'{supplier_name}_Tổng giá'] = 'Không báo giá'
comparison_table.append(comparison_item)
return comparison_table
# Thực hiện so sánh
supplier_tables = [supplier_a_table, supplier_b_table]
supplier_names = ['Nhà cung cấp A', 'Nhà cung cấp B']
# Đảm bảo các bảng nhà cung cấp không rỗng trước khi truyền vào hàm
valid_supplier_tables = [tbl for tbl in supplier_tables if tbl]
valid_supplier_names = [name for i, name in enumerate(supplier_names) if supplier_tables[i]]
if demand_table and valid_supplier_tables:
comparison_result = compare_supplier_quotes(demand_table, valid_supplier_tables, valid_supplier_names)
# In kết quả so sánh
print("\nKết quả so sánh báo giá:")
for item in comparison_result:
print(f"Sản phẩm: {item['Mã sản phẩm']}, Số lượng: {item['Số lượng yêu cầu']}")
for supplier in valid_supplier_names:
price = item.get(f'{supplier}_Đơn giá', 'N/A')
total = item.get(f'{supplier}_Tổng giá', 'N/A')
delivery = item.get(f'{supplier}_Thời gian giao hàng', 'N/A')
print(f" {supplier}: Đơn giá {price}, Tổng giá {total}, Giao hàng: {delivery}")
print()
else:
print("Không đủ dữ liệu để thực hiện so sánh.")
Tạo báo cáo so sánh
Với dữ liệu so sánh, chúng ta có thể yêu cầu mô hình tạo báo cáo phân tích:
def generate_comparison_report(comparison_table, supplier_names):
"""Tạo báo cáo phân tích so sánh báo giá."""
# Chuyển đổi dữ liệu so sánh thành định dạng văn bản để mô hình phân tích
comparison_text = "Dữ liệu so sánh báo giá:\n\n"
for item in comparison_table:
comparison_text += f"Mã sản phẩm: {item['Mã sản phẩm']}, Số lượng yêu cầu: {item['Số lượng yêu cầu']}\n"
for supplier in supplier_names:
price = item.get(f'{supplier}_Đơn giá', 'Không báo giá')
total = item.get(f'{supplier}_Tổng giá', 'Không báo giá')
delivery = item.get(f'{supplier}_Thời gian giao hàng', 'Không báo giá')
comparison_text += f" {supplier}: Đơn giá {price}, Tổng giá {total}, Giao hàng: {delivery}\n"
comparison_text += "\n"
# Yêu cầu mô hình phân tích dữ liệu
prompt = f"""
Vui lòng phân tích dữ liệu so sánh báo giá của các nhà cung cấp sau và đưa ra đề xuất mua hàng.
{comparison_text}
Vui lòng phân tích dựa trên các khía cạnh sau:
1. So sánh giá: Nhà cung cấp nào có lợi thế về giá tổng thể?
2. Khả năng cung cấp: Nhà cung cấp nào báo giá đầy đủ hơn?
3. So sánh thời gian giao hàng: Nhà cung cấp nào giao hàng nhanh hơn?
4. Đề xuất tổng hợp: Nên chọn nhà cung cấp nào và tại sao?
Vui lòng trình bày kết quả phân tích một cách ngắn gọn và rõ ràng.
"""
messages = [
{
"role": "user",
"content": prompt
}
]
response = ollama.chat(
model='qwen2.5vl:7b',
messages=messages
)
return response['message']['content']
# Tạo báo cáo
if comparison_result and valid_supplier_names:
report = generate_comparison_report(comparison_result, valid_supplier_names)
print("\nBáo cáo phân tích so sánh báo giá:")
print(report)
else:
print("Không có dữ liệu để tạo báo cáo.")
Tối ưu hóa hiệu suất và Mẹo sử dụng
Trong quá trình sử dụng thực tế, bạn có thể gặp phải các vấn đề về hiệu suất hoặc độ chính xác. Dưới đây là một vài mẹo tối ưu hóa tôi đã tổng hợp được.
Tối ưu xử lý hàng loạt
Nếu có nhiều ảnh cần xử lý, gọi mô hình cho từng ảnh sẽ rất chậm. Chúng ta có thể xử lý hàng loạt:
import time
def batch_process_images(image_paths, batch_size=3):
"""Xử lý hàng loạt ảnh để tăng hiệu quả."""
all_results = []
for i in range(0, len(image_paths), batch_size):
batch_paths = image_paths[i:i+batch_size]
print(f"Đang xử lý lô {i//batch_size + 1}: {len(batch_paths)} ảnh")
batch_results = []
for image_path in batch_paths:
try:
# Sử dụng mô tả chung cho các ảnh
result = extract_table_from_image(
image_path,
"Vui lòng nhận diện dữ liệu bảng trong ảnh."
)
batch_results.append({
'image': image_path,
'data': result,
'status': 'Thành công'
})
except Exception as e:
batch_results.append({
'image': image_path,
'data': None,
'status': f'Thất bại: {str(e)}'
})
all_results.extend(batch_results)
# Nghỉ một chút giữa các lô để tránh quá tải
if i + batch_size < len(image_paths):
time.sleep(1) # Chờ 1 giây
return all_results
# Ví dụ sử dụng:
# image_files = ["img1.png", "img2.png", "img3.png", "img4.png", ...]
# results = batch_process_images(image_files, batch_size=4)
# print(results)
Nâng cao độ chính xác khi nhận diện
Đôi khi mô hình có thể nhận diện không chính xác, đặc biệt với các bảng có cấu trúc phức tạp. Trong trường hợp này, bạn có thể:
- Cung cấp gợi ý chi tiết hơn: Mô tả rõ hơn cấu trúc bảng cho mô hình.
- Nhận diện tiêu đề trước: Yêu cầu mô hình nhận diện tiêu đề trước, sau đó mới nhận diện dữ liệu.
- Xử lý hậu kiểm: Kiểm tra kết quả nhận diện theo logic.
def extract_table_with_validation(image_path):
"""Trích xuất bảng kèm kiểm tra."""
# Bước 1: Nhận diện tiêu đề bảng
header_prompt = """
Vui lòng nhận diện dòng tiêu đề của bảng trong ảnh.
Xuất danh sách các trường tiêu đề.
"""
# Bước 2: Nhận diện toàn bộ bảng
table_prompt = """
Vui lòng nhận diện toàn bộ dữ liệu bảng.
Chú ý giữ gìn sự nhất quán của dữ liệu.
"""
# Bước 3: Kiểm tra logic
validation_prompt = """
Vui lòng kiểm tra tính hợp lý của dữ liệu bảng sau đây:
1. Kiểu dữ liệu của cùng một cột có nhất quán không?
2. Có lỗi nhận diện rõ ràng nào không?
3. Định dạng số có chính xác không?
Vui lòng xuất kết quả kiểm tra và đề xuất sửa đổi.
"""
# Tại đây, bạn có thể gọi mô hình theo từng bước hoặc thiết kế logic kiểm tra phức tạp hơn
# ...
pass # Placeholder
Xử lý bảng phức tạp
Đối với các bảng đặc biệt phức tạp (ví dụ: ô gộp, tiêu đề nhiều cấp), có thể cần xử lý đặc biệt:
def extract_complex_table(image_path):
"""Xử lý bảng phức tạp."""
prompt = """
Vui lòng nhận diện bảng phức tạp này.
Đặc biệt chú ý:
1. Ô gộp: Mở rộng nội dung sang từng ô tương ứng.
2. Tiêu đề nhiều cấp: Nhận diện mối quan hệ phân cấp của tiêu đề.
3. Dòng tổng phụ/tổng cộng: Đánh dấu các dòng này.
4. Ô trống: Sử dụng null hoặc chuỗi rỗng.
Khi xuất, vui lòng giữ nguyên cấu trúc đầy đủ của bảng.
"""
# Gọi mô hình
# ...
pass # Placeholder
Mở rộng các trường hợp ứng dụng thực tế
Ngoài việc so sánh báo giá nhà cung cấp, công nghệ này còn có thể áp dụng trong nhiều lĩnh vực khác:
Kiểm tra báo cáo tài chính
Nhân viên tài chính thường xuyên phải kiểm tra các báo cáo có định dạng khác nhau. Ví dụ: sao kê ngân hàng, hóa đơn, phiếu chi. Các định dạng này không nhất quán. Sử dụng phương pháp này giúp trích xuất dữ liệu nhanh chóng để đối chiếu.
def compare_financial_reports(report1_image, report2_image, report1_desc, report2_desc):
"""So sánh hai báo cáo tài chính."""
# Trích xuất dữ liệu từ hai báo cáo
report1_data = extract_table_from_image(report1_image, report1_desc)
report2_data = extract_table_from_image(report2_image, report2_desc)
# Đối chiếu các chỉ số quan trọng
# Ví dụ: doanh thu, chi phí, lợi nhuận, v.v.
# ...
pass # Placeholder
Nhận diện phiếu trả lời trắc nghiệm
Các cơ sở giáo dục có thể sử dụng công nghệ này để nhận diện phiếu trả lời trắc nghiệm (viết tay hoặc in sẵn), sau đó so sánh với đáp án chuẩn để chấm điểm tự động.
Kiểm kê kho hàng
Nhân viên kho chụp ảnh kệ hàng, mô hình nhận diện sản phẩm và số lượng, sau đó so sánh với số liệu trong hệ thống để phát hiện sai lệch.
Kết luận
Sử dụng Qwen2.5-VL-7B-Instruct để triển khai đối chiếu dữ liệu giữa các bảng giống như VLOOKUP, thoạt nhìn có vẻ phức tạp, nhưng thực tế lại giải quyết được nhiều vấn đề mà các phương pháp truyền thống không làm được. Đặc biệt là khi nguồn dữ liệu đa dạng và định dạng không nhất quán, ưu điểm của phương pháp này sẽ được thể hiện rõ.
Từ kinh nghiệm sử dụng thực tế, tôi nhận thấy một số lợi ích rõ rệt:
- Tính linh hoạt cực cao: Có thể xử lý dữ liệu dưới dạng Excel, PDF hay hình ảnh. Điều này rất hữu ích cho việc tích hợp dữ liệu từ nhiều nguồn.
- Độ chính xác tốt: Miễn là ảnh rõ nét và cấu trúc bảng không quá phức tạp, mô hình nhận diện khá chính xác. Mô hình còn có thể hiểu ngữ nghĩa của bảng, ví dụ như nhận biết đâu là tiêu đề, đâu là dữ liệu.
- Khả năng tự động hóa: Khi quy trình được thiết lập, các công việc tương tự sau này có thể được tự động hóa, tiết kiệm thời gian đáng kể.
Tuy nhiên, phương pháp này không phải là hoàn hảo. Nếu chất lượng ảnh quá kém hoặc bảng quá phức tạp, hiệu quả nhận diện sẽ bị ảnh hưởng. Trong trường hợp đó, có thể cần sự can thiệp thủ công hoặc kết hợp với các công nghệ khác.
Nếu bạn thường xuyên phải xử lý dữ liệu bảng với nhiều định dạng khác nhau, đặc biệt là cần đối chiếu giữa các bảng, tôi khuyên bạn nên thử phương pháp này. Ban đầu có thể thấy hơi phức tạp, nhưng khi đã làm quen, hiệu suất công việc sẽ cải thiện đáng kể.
Quan trọng nhất, công nghệ này đang phát triển nhanh chóng. Với khả năng ngày càng tăng của mô hình, trong tương lai, có thể chỉ cần một câu lệnh là có thể hoàn thành các tác vụ xử lý bảng phức tạp.