Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f7338175f | ||
|
|
6781c00d5b | ||
|
|
dd7b0644d5 | ||
|
|
2a13f0d985 | ||
|
|
cb38d413ad | ||
|
|
da455791de | ||
|
|
61dd2dcaad | ||
|
|
66c9194fcf | ||
|
|
1e8466f7a8 | ||
|
|
ac3372d2d2 | ||
|
|
17156516a0 | ||
|
|
1eb323e959 | ||
|
|
3c7fcc010f | ||
|
|
becce857e1 | ||
|
|
3672c9343e | ||
|
|
4efbb7f4b8 | ||
|
|
bbb662acd1 | ||
|
|
9e68f2e1d3 | ||
|
|
b6913d2f93 |
@@ -61,3 +61,5 @@ node_modules/
|
||||
|
||||
# Sensitive/large data
|
||||
*.xlsx
|
||||
|
||||
.serena/
|
||||
|
||||
@@ -1,432 +0,0 @@
|
||||
# 新对话交接文档 - PP-OCRv5研究
|
||||
|
||||
**日期**: 2025-10-29
|
||||
**前序对话**: PaddleOCR-Cover分支开发
|
||||
**当前分支**: `paddleocr-improvements` (稳定)
|
||||
**新分支**: `pp-ocrv5-research` (待创建)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 任务目标
|
||||
|
||||
研究和实现 **PP-OCRv5** 的手写签名检测功能
|
||||
|
||||
---
|
||||
|
||||
## 📋 背景信息
|
||||
|
||||
### 当前状况
|
||||
|
||||
✅ **已有稳定方案** (`paddleocr-improvements` 分支):
|
||||
- PaddleOCR 2.7.3 + OpenCV Method 3
|
||||
- 86.5%手写保留率
|
||||
- 区域合并算法工作良好
|
||||
- 测试: 1个PDF成功检测2个签名
|
||||
|
||||
⚠️ **PP-OCRv5升级遇到问题**:
|
||||
- PaddleOCR 3.3.0 API完全改变
|
||||
- 旧服务器代码不兼容
|
||||
- 需要深入研究新API
|
||||
|
||||
### 为什么要研究PP-OCRv5?
|
||||
|
||||
**文档显示**: https://www.paddleocr.ai/main/en/version3.x/algorithm/PP-OCRv5/PP-OCRv5.html
|
||||
|
||||
PP-OCRv5性能提升:
|
||||
- 手写中文检测: **0.706 → 0.803** (+13.7%)
|
||||
- 手写英文检测: **0.249 → 0.841** (+237%)
|
||||
- 可能支持直接输出手写区域坐标
|
||||
|
||||
**潜在优势**:
|
||||
1. 更好的手写识别能力
|
||||
2. 可能内置手写/印刷分类
|
||||
3. 更准确的坐标输出
|
||||
4. 减少复杂的后处理
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术栈
|
||||
|
||||
### 服务器环境
|
||||
|
||||
```
|
||||
Host: 192.168.30.36 (Linux GPU服务器)
|
||||
SSH: ssh gblinux
|
||||
目录: ~/Project/paddleocr-server/
|
||||
```
|
||||
|
||||
**当前稳定版本**:
|
||||
- PaddleOCR: 2.7.3
|
||||
- numpy: 1.26.4
|
||||
- opencv-contrib-python: 4.6.0.66
|
||||
- 服务器文件: `paddleocr_server.py`
|
||||
|
||||
**已安装但未使用**:
|
||||
- PaddleOCR 3.3.0 (PP-OCRv5)
|
||||
- 临时服务器: `paddleocr_server_v5.py` (未完成)
|
||||
|
||||
### 本地环境
|
||||
|
||||
```
|
||||
macOS
|
||||
Python: 3.14
|
||||
虚拟环境: venv/
|
||||
客户端: paddleocr_client.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 核心问题
|
||||
|
||||
### 1. API变更
|
||||
|
||||
**旧API (2.7.3)**:
|
||||
```python
|
||||
from paddleocr import PaddleOCR
|
||||
ocr = PaddleOCR(lang='ch')
|
||||
result = ocr.ocr(image_np, cls=False)
|
||||
|
||||
# 返回格式:
|
||||
# [[[box], (text, confidence)], ...]
|
||||
```
|
||||
|
||||
**新API (3.3.0)** - ⚠️ 未完全理解:
|
||||
```python
|
||||
# 方式1: 传统方式 (Deprecated)
|
||||
result = ocr.ocr(image_np) # 警告: Please use predict instead
|
||||
|
||||
# 方式2: 新方式
|
||||
from paddlex import create_model
|
||||
model = create_model("???") # 模型名未知
|
||||
result = model.predict(image_np)
|
||||
|
||||
# 返回格式: ???
|
||||
```
|
||||
|
||||
### 2. 遇到的错误
|
||||
|
||||
**错误1**: `cls` 参数不再支持
|
||||
```python
|
||||
# 错误: PaddleOCR.predict() got an unexpected keyword argument 'cls'
|
||||
result = ocr.ocr(image_np, cls=False) # ❌
|
||||
```
|
||||
|
||||
**错误2**: 返回格式改变
|
||||
```python
|
||||
# 旧代码解析失败:
|
||||
text = item[1][0] # ❌ IndexError
|
||||
confidence = item[1][1] # ❌ IndexError
|
||||
```
|
||||
|
||||
**错误3**: 模型名称错误
|
||||
```python
|
||||
model = create_model("PP-OCRv5_server") # ❌ Model not supported
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 研究任务清单
|
||||
|
||||
### Phase 1: API研究 (优先级高)
|
||||
|
||||
- [ ] **阅读官方文档**
|
||||
- PP-OCRv5完整文档
|
||||
- PaddleX API文档
|
||||
- 迁移指南 (如果有)
|
||||
|
||||
- [ ] **理解新API**
|
||||
```python
|
||||
# 需要搞清楚:
|
||||
1. 正确的导入方式
|
||||
2. 模型初始化方法
|
||||
3. predict()参数和返回格式
|
||||
4. 如何区分手写/印刷
|
||||
5. 是否有手写检测专用功能
|
||||
```
|
||||
|
||||
- [ ] **编写测试脚本**
|
||||
- `test_pp_ocrv5_api.py` - 测试基础API调用
|
||||
- 打印完整的result数据结构
|
||||
- 对比v4和v5的返回差异
|
||||
|
||||
### Phase 2: 服务器适配
|
||||
|
||||
- [ ] **重写服务器代码**
|
||||
- 适配新API
|
||||
- 正确解析返回数据
|
||||
- 保持REST接口兼容
|
||||
|
||||
- [ ] **测试稳定性**
|
||||
- 测试10个PDF样本
|
||||
- 检查GPU利用率
|
||||
- 对比v4性能
|
||||
|
||||
### Phase 3: 手写检测功能
|
||||
|
||||
- [ ] **查找手写检测能力**
|
||||
```python
|
||||
# 可能的方式:
|
||||
1. result中是否有 text_type 字段?
|
||||
2. 是否有专门的 handwriting_detection 模型?
|
||||
3. 是否有置信度差异可以利用?
|
||||
4. PP-Structure 的 layout 分析?
|
||||
```
|
||||
|
||||
- [ ] **对比测试**
|
||||
- v4 (当前方案) vs v5
|
||||
- 准确率、召回率、速度
|
||||
- 手写检测能力
|
||||
|
||||
### Phase 4: 集成决策
|
||||
|
||||
- [ ] **性能评估**
|
||||
- 如果v5更好 → 升级
|
||||
- 如果改进不明显 → 保持v4
|
||||
|
||||
- [ ] **文档更新**
|
||||
- 记录v5使用方法
|
||||
- 更新PADDLEOCR_STATUS.md
|
||||
|
||||
---
|
||||
|
||||
## 🔍 调试技巧
|
||||
|
||||
### 1. 查看完整返回数据
|
||||
|
||||
```python
|
||||
import pprint
|
||||
result = model.predict(image)
|
||||
pprint.pprint(result) # 完整输出所有字段
|
||||
|
||||
# 或者
|
||||
import json
|
||||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||||
```
|
||||
|
||||
### 2. 查找官方示例
|
||||
|
||||
```bash
|
||||
# 在服务器上查找PaddleOCR安装示例
|
||||
find ~/Project/paddleocr-server/venv/lib/python3.12/site-packages/paddleocr -name "*.py" | grep example
|
||||
|
||||
# 查看源码
|
||||
less ~/Project/paddleocr-server/venv/lib/python3.12/site-packages/paddleocr/paddleocr.py
|
||||
```
|
||||
|
||||
### 3. 查看可用模型
|
||||
|
||||
```python
|
||||
from paddlex.inference.models import OFFICIAL_MODELS
|
||||
print(OFFICIAL_MODELS) # 列出所有支持的模型名
|
||||
```
|
||||
|
||||
### 4. Web文档搜索
|
||||
|
||||
重点查看:
|
||||
- https://github.com/PaddlePaddle/PaddleOCR
|
||||
- https://www.paddleocr.ai
|
||||
- https://github.com/PaddlePaddle/PaddleX
|
||||
|
||||
---
|
||||
|
||||
## 📂 文件结构
|
||||
|
||||
```
|
||||
/Volumes/NV2/pdf_recognize/
|
||||
├── CURRENT_STATUS.md # 当前状态文档 ✅
|
||||
├── NEW_SESSION_HANDOFF.md # 本文件 ✅
|
||||
├── PADDLEOCR_STATUS.md # 详细技术文档 ✅
|
||||
├── SESSION_INIT.md # 初始会话信息
|
||||
│
|
||||
├── paddleocr_client.py # 稳定客户端 (v2.7.3) ✅
|
||||
├── paddleocr_server_v5.py # v5服务器 (未完成) ⚠️
|
||||
│
|
||||
├── test_paddleocr_client.py # 基础测试
|
||||
├── test_mask_and_detect.py # 遮罩+检测
|
||||
├── test_opencv_separation.py # Method 1+2
|
||||
├── test_opencv_advanced.py # Method 3 (最佳) ✅
|
||||
├── extract_signatures_paddleocr_improved.py # 完整Pipeline
|
||||
│
|
||||
└── check_rejected_for_missing.py # 诊断脚本
|
||||
```
|
||||
|
||||
**服务器端** (`ssh gblinux`):
|
||||
```
|
||||
~/Project/paddleocr-server/
|
||||
├── paddleocr_server.py # v2.7.3稳定版 ✅
|
||||
├── paddleocr_server_v5.py # v5版本 (待完成) ⚠️
|
||||
├── paddleocr_server_backup.py # 备份
|
||||
├── server_stable.log # 当前运行日志
|
||||
└── venv/ # 虚拟环境
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚡ 快速启动
|
||||
|
||||
### 启动稳定服务器 (v2.7.3)
|
||||
|
||||
```bash
|
||||
ssh gblinux
|
||||
cd ~/Project/paddleocr-server
|
||||
source venv/bin/activate
|
||||
python paddleocr_server.py
|
||||
```
|
||||
|
||||
### 测试连接
|
||||
|
||||
```bash
|
||||
# 本地Mac
|
||||
cd /Volumes/NV2/pdf_recognize
|
||||
source venv/bin/activate
|
||||
python test_paddleocr_client.py
|
||||
```
|
||||
|
||||
### 创建新研究分支
|
||||
|
||||
```bash
|
||||
cd /Volumes/NV2/pdf_recognize
|
||||
git checkout -b pp-ocrv5-research
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
### 1. 不要破坏稳定版本
|
||||
|
||||
- `paddleocr-improvements` 分支保持稳定
|
||||
- 所有v5实验在新分支 `pp-ocrv5-research`
|
||||
- 服务器保留 `paddleocr_server.py` (v2.7.3)
|
||||
- 新代码命名: `paddleocr_server_v5.py`
|
||||
|
||||
### 2. 环境隔离
|
||||
|
||||
- 服务器虚拟环境可能需要重建
|
||||
- 或者用Docker隔离v4和v5
|
||||
- 避免版本冲突
|
||||
|
||||
### 3. 性能测试
|
||||
|
||||
- 记录v4和v5的具体指标
|
||||
- 至少测试10个样本
|
||||
- 包括速度、准确率、召回率
|
||||
|
||||
### 4. 文档驱动
|
||||
|
||||
- 每个发现记录到文档
|
||||
- API用法写清楚
|
||||
- 便于未来维护
|
||||
|
||||
---
|
||||
|
||||
## 📊 成功标准
|
||||
|
||||
### 最低目标
|
||||
|
||||
- [ ] 成功运行PP-OCRv5基础OCR
|
||||
- [ ] 理解新API调用方式
|
||||
- [ ] 服务器稳定运行
|
||||
- [ ] 记录完整文档
|
||||
|
||||
### 理想目标
|
||||
|
||||
- [ ] 发现手写检测功能
|
||||
- [ ] 性能超过v4方案
|
||||
- [ ] 简化Pipeline复杂度
|
||||
- [ ] 提升准确率 > 90%
|
||||
|
||||
### 决策点
|
||||
|
||||
**如果v5明显更好** → 升级到v5,废弃v4
|
||||
**如果v5改进不明显** → 保持v4,v5仅作研究记录
|
||||
**如果v5有bug** → 等待官方修复,暂用v4
|
||||
|
||||
---
|
||||
|
||||
## 📞 问题排查
|
||||
|
||||
### 遇到问题时
|
||||
|
||||
1. **先查日志**: `tail -f ~/Project/paddleocr-server/server_stable.log`
|
||||
2. **查看源码**: 在venv里找PaddleOCR代码
|
||||
3. **搜索Issues**: https://github.com/PaddlePaddle/PaddleOCR/issues
|
||||
4. **降级测试**: 确认v2.7.3是否还能用
|
||||
|
||||
### 常见问题
|
||||
|
||||
**Q: 服务器启动失败?**
|
||||
A: 检查numpy版本 (需要 < 2.0)
|
||||
|
||||
**Q: 找不到模型?**
|
||||
A: 模型名可能变化,查看OFFICIAL_MODELS
|
||||
|
||||
**Q: API调用失败?**
|
||||
A: 对比官方文档,可能参数格式变化
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习资源
|
||||
|
||||
### 官方文档
|
||||
|
||||
1. **PP-OCRv5**: https://www.paddleocr.ai/main/en/version3.x/algorithm/PP-OCRv5/PP-OCRv5.html
|
||||
2. **PaddleOCR GitHub**: https://github.com/PaddlePaddle/PaddleOCR
|
||||
3. **PaddleX**: https://github.com/PaddlePaddle/PaddleX
|
||||
|
||||
### 相关技术
|
||||
|
||||
- PaddlePaddle深度学习框架
|
||||
- PP-Structure文档结构分析
|
||||
- 手写识别 (Handwriting Recognition)
|
||||
- 版面分析 (Layout Analysis)
|
||||
|
||||
---
|
||||
|
||||
## 💡 提示
|
||||
|
||||
### 如果发现内置手写检测
|
||||
|
||||
可能的用法:
|
||||
```python
|
||||
# 猜测1: 返回结果包含类型
|
||||
for item in result:
|
||||
text_type = item.get('type') # 'printed' or 'handwritten'?
|
||||
|
||||
# 猜测2: 专门的layout模型
|
||||
from paddlex import create_model
|
||||
layout_model = create_model("PP-Structure")
|
||||
layout_result = layout_model.predict(image)
|
||||
# 可能返回: text, handwriting, figure, table...
|
||||
|
||||
# 猜测3: 置信度差异
|
||||
# 手写文字置信度可能更低
|
||||
```
|
||||
|
||||
### 如果没有内置手写检测
|
||||
|
||||
那么当前OpenCV Method 3仍然是最佳方案,v5仅提供更好的OCR准确度。
|
||||
|
||||
---
|
||||
|
||||
## ✅ 完成检查清单
|
||||
|
||||
研究完成后,确保:
|
||||
|
||||
- [ ] 新API用法完全理解并文档化
|
||||
- [ ] 服务器代码重写并测试通过
|
||||
- [ ] 性能对比数据记录
|
||||
- [ ] 决策文档 (升级 vs 保持v4)
|
||||
- [ ] 代码提交到 `pp-ocrv5-research` 分支
|
||||
- [ ] 更新 `CURRENT_STATUS.md`
|
||||
- [ ] 如果升级: 合并到main分支
|
||||
|
||||
---
|
||||
|
||||
**祝研究顺利!** 🚀
|
||||
|
||||
有问题随时查阅:
|
||||
- `CURRENT_STATUS.md` - 当前方案详情
|
||||
- `PADDLEOCR_STATUS.md` - 技术细节和问题分析
|
||||
|
||||
**最重要**: 记录所有发现,无论成功或失败,都是宝贵经验!
|
||||
@@ -1,195 +0,0 @@
|
||||
# Session Handoff Checklist ✓
|
||||
|
||||
## Before You Exit This Session
|
||||
|
||||
- [x] All documentation written
|
||||
- [x] Test results recorded (7/10 signatures, 70% recall)
|
||||
- [x] Session initialization files created
|
||||
- [x] .gitignore configured
|
||||
- [x] Commit guide prepared
|
||||
- [ ] **Git commit performed** (waiting for user approval)
|
||||
|
||||
## Files Created for Next Session
|
||||
|
||||
### Essential Files ⭐
|
||||
- [x] **SESSION_INIT.md** - Read this first in next session
|
||||
- [x] **NEW_SESSION_PROMPT.txt** - Copy-paste prompt template
|
||||
- [x] **PROJECT_DOCUMENTATION.md** - Complete 24KB history
|
||||
- [x] **HOW_TO_CONTINUE.txt** - Visual guide
|
||||
|
||||
### Supporting Files
|
||||
- [x] README.md - Quick start guide
|
||||
- [x] COMMIT_SUMMARY.md - Git instructions
|
||||
- [x] README_page_extraction.md - Page extraction docs
|
||||
- [x] README_hybrid_extraction.md - Signature extraction docs
|
||||
- [x] .gitignore - Configured properly
|
||||
|
||||
### Working Scripts
|
||||
- [x] extract_pages_from_csv.py - Tested (100 files)
|
||||
- [x] extract_signatures_hybrid.py - Tested (5 files, 70% recall)
|
||||
- [x] extract_handwriting.py - Component script
|
||||
|
||||
## What's Working ✅
|
||||
|
||||
| Component | Status | Details |
|
||||
|-----------|--------|---------|
|
||||
| Page extraction | ✅ Working | 100 files tested |
|
||||
| VLM name extraction | ✅ Working | 100% accurate on 5 files |
|
||||
| CV detection | ⚠️ Conservative | Finds 70% of signatures |
|
||||
| VLM verification | ✅ Working | 100% precision, no false positives |
|
||||
| Overall system | ✅ Working | 70% recall, 100% precision |
|
||||
|
||||
## What's Not Working / Unknown ⚠️
|
||||
|
||||
| Issue | Status | Next Steps |
|
||||
|-------|--------|------------|
|
||||
| Missing 30% signatures | Known | Tune CV parameters |
|
||||
| Text layer method | Untested | Need PDFs with text |
|
||||
| Large-scale performance | Unknown | Test with 100+ files |
|
||||
| Full dataset (86K) | Unknown | Estimate time & optimize |
|
||||
|
||||
## Critical Context to Remember 🧠
|
||||
|
||||
1. **VLM coordinates are unreliable** (32% offset on test file)
|
||||
- Don't use VLM for location detection
|
||||
- Use VLM for name extraction only
|
||||
|
||||
2. **Name-based approach is the solution**
|
||||
- VLM extracts names ✓
|
||||
- CV finds locations ✓
|
||||
- VLM verifies regions ✓
|
||||
|
||||
3. **Test file with coordinate issue:**
|
||||
- `201301_2458_AI1_page4.pdf`
|
||||
- VLM found 2 names but coordinates pointed to blank areas
|
||||
- Actual signatures at 26% (reported as 58% and 68%)
|
||||
|
||||
## To Start Next Session
|
||||
|
||||
### Simple Method (Recommended)
|
||||
```bash
|
||||
cat /Volumes/NV2/pdf_recognize/NEW_SESSION_PROMPT.txt
|
||||
# Copy output and paste to new Claude Code session
|
||||
```
|
||||
|
||||
### Manual Method
|
||||
Tell Claude:
|
||||
> "I'm continuing the PDF signature extraction project at `/Volumes/NV2/pdf_recognize/`. Please read `SESSION_INIT.md` and `PROJECT_DOCUMENTATION.md` to understand the current state. I want to [choose option from SESSION_INIT.md]."
|
||||
|
||||
## Quick Commands Reference
|
||||
|
||||
### View Documentation
|
||||
```bash
|
||||
less /Volumes/NV2/pdf_recognize/SESSION_INIT.md
|
||||
less /Volumes/NV2/pdf_recognize/PROJECT_DOCUMENTATION.md
|
||||
```
|
||||
|
||||
### Run Scripts
|
||||
```bash
|
||||
cd /Volumes/NV2/pdf_recognize
|
||||
source venv/bin/activate
|
||||
python extract_signatures_hybrid.py # Main script
|
||||
```
|
||||
|
||||
### Check Results
|
||||
```bash
|
||||
ls -lh /Volumes/NV2/PDF-Processing/signature-image-output/signatures/*.png
|
||||
```
|
||||
|
||||
### View Session Handoff
|
||||
```bash
|
||||
cat /Volumes/NV2/pdf_recognize/HOW_TO_CONTINUE.txt
|
||||
```
|
||||
|
||||
## What Can Be Improved (Future Work)
|
||||
|
||||
### Priority 1: Increase Recall
|
||||
- Current: 70%
|
||||
- Target: 90%+
|
||||
- Method: Tune CV parameters in lines 178-214 of extract_signatures_hybrid.py
|
||||
|
||||
### Priority 2: Scale Testing
|
||||
- Current: 5 files tested
|
||||
- Next: 100 files
|
||||
- Future: 86,073 files (full dataset)
|
||||
|
||||
### Priority 3: Optimization
|
||||
- Current: ~24 seconds per PDF
|
||||
- Consider: Parallel processing, batch VLM calls
|
||||
|
||||
### Priority 4: Text Layer Testing
|
||||
- Current: Untested (all PDFs are scanned)
|
||||
- Need: Find PDFs with searchable text layer
|
||||
|
||||
## Verification Steps
|
||||
|
||||
Before next session, verify files exist:
|
||||
```bash
|
||||
cd /Volumes/NV2/pdf_recognize
|
||||
|
||||
# Check essential docs
|
||||
ls -lh SESSION_INIT.md PROJECT_DOCUMENTATION.md NEW_SESSION_PROMPT.txt
|
||||
|
||||
# Check working scripts
|
||||
ls -lh extract_pages_from_csv.py extract_signatures_hybrid.py
|
||||
|
||||
# Check test results
|
||||
ls /Volumes/NV2/PDF-Processing/signature-image-output/signatures/*.png | wc -l
|
||||
# Should show: 7 (the 7 verified signatures)
|
||||
```
|
||||
|
||||
## Known Good State
|
||||
|
||||
### Environment
|
||||
- Python: 3.9+ with venv
|
||||
- Ollama: http://192.168.30.36:11434
|
||||
- Model: qwen2.5vl:32b
|
||||
- Working directory: /Volumes/NV2/pdf_recognize/
|
||||
|
||||
### Test Data
|
||||
- 5 PDFs processed
|
||||
- 7 signatures extracted
|
||||
- All verified (100% precision)
|
||||
- 3 signatures missed (70% recall)
|
||||
|
||||
### Output Files
|
||||
```
|
||||
201301_1324_AI1_page3_signature_張志銘.png (33 KB)
|
||||
201301_1324_AI1_page3_signature_楊智惠.png (37 KB)
|
||||
201301_2061_AI1_page5_signature_廖阿甚.png (87 KB)
|
||||
201301_2458_AI1_page4_signature_周寶蓮.png (230 KB)
|
||||
201301_2923_AI1_page3_signature_黄瑞展.png (184 KB)
|
||||
201301_3189_AI1_page3_signature_黄益辉.png (24 KB)
|
||||
201301_3189_AI1_page3_signature_黄辉.png (84 KB)
|
||||
```
|
||||
|
||||
## Git Status (Pre-Commit)
|
||||
|
||||
Files staged for commit:
|
||||
- [ ] extract_pages_from_csv.py
|
||||
- [ ] extract_signatures_hybrid.py
|
||||
- [ ] extract_handwriting.py
|
||||
- [ ] README.md
|
||||
- [ ] PROJECT_DOCUMENTATION.md
|
||||
- [ ] README_page_extraction.md
|
||||
- [ ] README_hybrid_extraction.md
|
||||
- [ ] .gitignore
|
||||
|
||||
**Waiting for:** User to review docs and approve commit
|
||||
|
||||
## Session Health Check ✓
|
||||
|
||||
- [x] All scripts working
|
||||
- [x] Test results documented
|
||||
- [x] Issues identified and recorded
|
||||
- [x] Next steps defined
|
||||
- [x] Session continuity files created
|
||||
- [x] Git commit prepared
|
||||
|
||||
**Status:** ✅ Ready for handoff
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** October 26, 2025
|
||||
**Session End:** Ready for next session
|
||||
**Next Action:** User reviews docs → Git commit → Continue work
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,114 @@
|
||||
[軸 1]
|
||||
|
||||
[MAJOR] §I Contributions, L42
|
||||
原句:「resolves the ambiguity between *style consistency* and *image reproduction*」
|
||||
問題:這比摘要語氣強。descriptor-only framework 不能真正「解開」style consistency 與 image reproduction 的機制歸因,§V-H 也說不能分離。
|
||||
修改建議:改為「provides complementary evidence for screening cases where style consistency and image reproduction hypotheses diverge」。
|
||||
|
||||
[MAJOR] §III-H.1, L314
|
||||
原句:「High-confidence non-hand-signed (HC)」
|
||||
問題:作為 rule label 可接受,但正文與表格反覆使用時,容易讀成已驗證分類結果,而非 screening label。
|
||||
修改建議:改為「High-confidence image-reuse screening label (HC)」,並在首次定義處明說「label names are operational labels, not ground-truth classes」。
|
||||
|
||||
[MAJOR] §III-H.1, L314
|
||||
原句:「Both descriptors converge on strong replication evidence.」
|
||||
問題:「strong replication evidence」過強;目前只保證兩個影像相似 descriptor 同時落入 rule box,不能保證 replication mechanism。
|
||||
修改建議:改為「Both descriptors converge on image-similarity evidence consistent with replication」。
|
||||
|
||||
[MAJOR] §III-H.1, L318
|
||||
原句:「Likely hand-signed (LH): Cosine $\leq 0.837$.」
|
||||
問題:沒有 hand-signed ground truth,不能把 low-similarity screening bin 命名成「likely hand-signed」而不冒認 ground-truth status。
|
||||
修改建議:改為「Low-replication-similarity (LRS)」或「Low-alert similarity」,保留舊縮寫可在括號說明。
|
||||
|
||||
[MAJOR] §V-C, L1060
|
||||
原句:「similar, milder production-related reuse patterns at Firms B/C/D」
|
||||
問題:這裡把 Firms B/C/D 的較溫和 within-firm collision 解讀為 production-related reuse,和 §V-H 的三機制不可分離聲明不一致。
|
||||
修改建議:改為「similar, milder within-firm collision patterns, whose mechanisms may include template reuse, digitisation-pipeline homogeneity, or signing-style homogeneity」。
|
||||
|
||||
[MINOR] §V-F, L1074
|
||||
原句:「the deployed five-way classifier is characterised at three units」
|
||||
問題:§V-H L1100 說 MC/HSC 與 document worst-case rule 未被本診斷組重新 characterise;這句像是整個 five-way classifier 都完成 ICCR calibration。
|
||||
修改建議:改為「the HC sub-rule and document-level alarm definitions derived from the five-way output are characterised...」。
|
||||
|
||||
[MINOR] §I Contributions, L44
|
||||
原句:「Composition decomposition disproves the distributional-threshold path.」
|
||||
問題:「disproves」語氣過硬;目前是對本資料與本診斷下不支持 natural-threshold reading。
|
||||
修改建議:改為「does not support」或「rules out within the tested diagnostics」。
|
||||
|
||||
[軸 2]
|
||||
|
||||
[MAJOR] §III-G vs §III-L, L286 / L458
|
||||
原句:「We analyse signatures at two units of resolution.」
|
||||
問題:§III-G 說兩個 units(signature/accountant),§III-L 又說 calibration 有三個 units(per-comparison/per-signature/per-document)。讀者第一次讀會混淆「statistical summary unit」與「calibration/reporting unit」。
|
||||
修改建議:在 §III-G 結尾加一個 bridge:accountant/signature 是 descriptor-summary units;§III-L 的 three units 是 ICCR reporting units。
|
||||
|
||||
[MAJOR] §III-H.2, L326
|
||||
原句:「The calibration distinguishes two reference populations」
|
||||
問題:Firm A 後文反覆說不是 calibration anchor;這句仍像 v3 殘留,讓 Firm A 看起來參與 threshold calibration。
|
||||
修改建議:改為「The supporting diagnostics use two reference populations」。
|
||||
|
||||
[MAJOR] §III-H.1 / §III-L, L320 / L456
|
||||
原句:「retain their prior calibration provenance」
|
||||
問題:§III-L 說本分析不 re-derive thresholds,但標題仍叫 threshold calibration,且 §III-H.1 只在 L320 一句帶過。第一次閱讀時不夠清楚:deployed 5-way rule 是既有 rule,ICCR 是行為刻畫,不是重新最佳化。
|
||||
修改建議:在 §III-H.1 後加一小段明確列出:「rule definition」「what §III-L calibrates」「what remains from supplement」。
|
||||
|
||||
[MAJOR] §III-I.2 / §III-J, L342 / L369
|
||||
原句:「K=2 / K=3 Gaussian mixture fits」
|
||||
問題:K=2/K=3 數字、BIC、解讀在 §III-I.2 與 §III-J 重複,仍有 v3 splice 的疊床架屋感。
|
||||
修改建議:§III-I.2 只保留「mixture path checked and demoted」摘要,完整模型細節集中到 §III-J。
|
||||
|
||||
[MINOR] §III-K, L432
|
||||
原句:「Leave-one-firm-out reproducibility ... Discussed in §III-J above.」
|
||||
問題:LOOO 已在 §III-J 詳述,又在 §III-K 作為 internal-consistency check;分類上不自然,且增加重複。
|
||||
修改建議:把 §III-K.3 改成單句 cross-reference,或移回 §III-J。
|
||||
|
||||
[MINOR] §III-L.1, L489
|
||||
原句:「dHash provides $\sim 4.3\times$ further per-comparison specificity」
|
||||
問題:這裡漏了 proxy/disclaimer;全文已避免 FAR,但「specificity」單獨出現會弱化 ICCR 語氣。
|
||||
修改建議:改為「specificity-proxy refinement」或「ICCR refinement」。
|
||||
|
||||
[MINOR] §III-L.2, L513
|
||||
原句:「consistent with the $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ form」
|
||||
問題:這是有用直覺,但 independence limit 與 within-firm violation 的關係應在同段提醒,否則會像正式模型。
|
||||
修改建議:補一句「This is an intuition, not an independence assumption used for estimation」。
|
||||
|
||||
[軸 3]
|
||||
|
||||
[MAJOR] §III-F, L277
|
||||
原句:「Hand-signing, by contrast, often yields high dHash similarity」
|
||||
問題:這句預設同一 CPA 多次親簽時「overall layout typically preserved」,接近不應預設的個別 CPA 跨文件一致性。雖然用 often,但仍在方法動機處承擔了未驗證手寫行為。
|
||||
修改建議:改為「One working hypothesis is that some hand-signed repetitions may preserve coarse layout while varying in fine execution; the classifier does not require this to hold for all CPAs」。
|
||||
|
||||
[MAJOR] §III-H.1, L316
|
||||
原句:「consistent with a CPA who signs very consistently」
|
||||
問題:HSC 被解讀成「同一 CPA 簽名很一致但非 reproduction」,這直接把高 cosine / 高 dHash 的 same-CPA pattern 歸因到個人書寫一致性。
|
||||
修改建議:改為「high feature similarity without structural corroboration; mechanism unresolved」。
|
||||
|
||||
[MAJOR] §III-H.1, L318
|
||||
原句:「Likely hand-signed」
|
||||
問題:低 max-cosine 並不等於親簽;也可能是跨年度書寫變化、掃描/PDF pipeline、裁切或多 template variant。這是對「沒有高 same-CPA match」的過度解讀。
|
||||
修改建議:改成 descriptor-based label,例如「low-replication-similarity」。
|
||||
|
||||
[MINOR] §III-G A1, L292
|
||||
原句:「within the cross-year same-CPA pool」
|
||||
問題:A1 本身不是年度一致性假設,但「cross-year」容易被讀成跨年度簽名應可比或應一致。
|
||||
修改建議:改為「within the observed same-CPA candidate pool pooled over years; this does not assume temporal stability of handwriting or scanning」。
|
||||
|
||||
[MINOR] §III-L.6, L587
|
||||
原句:「same-CPA repeatability signal」
|
||||
問題:已加 caveat,但「repeatability」仍可能被讀成個人簽名一致性訊號。
|
||||
修改建議:改為「observed same-CPA-pool excess signal, whose sources are not identifiable」。
|
||||
|
||||
[MINOR] §IV-M.6, L1043
|
||||
原句:「interpreted as a same-CPA repeatability signal」
|
||||
問題:同上,且出現在 results consolidation,容易被當成結果主張。
|
||||
修改建議:改為「reported as same-CPA-pool excess under §III-M caveats, not attributed to handwriting repeatability」。
|
||||
|
||||
總體判讀
|
||||
|
||||
軸 1 verdict:大方向已和摘要一致,但仍有幾個「validated detector / mechanism attribution」味道偏重的句子,尤其是「resolves ambiguity」、HC/LH label、§V-C 對 B/C/D 的 production-related reuse。
|
||||
軸 2 verdict:v3 殘留大多已被 demote,但 §III 的敘事仍偏重複;最大問題是 unit taxonomy 與 calibration/re-characterisation 範圍需要更早講清。
|
||||
軸 3 verdict:沒有發現核心計算邏輯必然依賴「同一 CPA 或跨年度簽名必須一致」;但若干命名與動機句會讓讀者以為有這個假設。
|
||||
|
||||
是否可送 partner 最終審查:可,但建議先做一輪小修,主要是改 label/語氣與 §III roadmap。
|
||||
BLOCKER:無。
|
||||
@@ -153,9 +153,19 @@ LATEX_TOKEN_REPLACEMENTS = [
|
||||
(r"\\leq(?![A-Za-z])", "≤"), (r"\\geq(?![A-Za-z])", "≥"),
|
||||
(r"\\neq(?![A-Za-z])", "≠"), (r"\\approx(?![A-Za-z])", "≈"),
|
||||
(r"\\equiv(?![A-Za-z])", "≡"), (r"\\sim(?![A-Za-z])", "~"),
|
||||
(r"\\in(?![A-Za-z])", "∈"), (r"\\notin(?![A-Za-z])", "∉"),
|
||||
(r"\\to(?![A-Za-z])", "→"), (r"\\rightarrow(?![A-Za-z])", "→"),
|
||||
(r"\\leftarrow(?![A-Za-z])", "←"), (r"\\Rightarrow(?![A-Za-z])", "⇒"),
|
||||
(r"\\Leftarrow(?![A-Za-z])", "⇐"),
|
||||
# Delimiters spelled as commands
|
||||
(r"\\lvert(?![A-Za-z])", "|"), (r"\\rvert(?![A-Za-z])", "|"),
|
||||
(r"\\lVert(?![A-Za-z])", "‖"), (r"\\rVert(?![A-Za-z])", "‖"),
|
||||
# Symbols / operators that linearise to text
|
||||
(r"\\checkmark(?![A-Za-z])", "✓"),
|
||||
(r"\\bullet(?![A-Za-z])", "•"),
|
||||
(r"\\max(?![A-Za-z])", "max"), (r"\\min(?![A-Za-z])", "min"),
|
||||
(r"\\log(?![A-Za-z])", "log"), (r"\\ln(?![A-Za-z])", "ln"),
|
||||
(r"\\exp(?![A-Za-z])", "exp"),
|
||||
# Binary operators
|
||||
(r"\\times(?![A-Za-z])", "×"), (r"\\cdot(?![A-Za-z])", "·"),
|
||||
(r"\\pm(?![A-Za-z])", "±"), (r"\\mp(?![A-Za-z])", "∓"),
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# 融合審查 — 修訂 TODO 清單
|
||||
|
||||
**來源**:`Fusion_Review_Crosscheck_Table.docx`(Gemini 3.1 Pro + ChatGPT 5.5 + Opus 4.8 → Opus 4.8 融合,2026-06-19)
|
||||
**對象稿件**:`paper/v13_build/paper_v13_filled.md`(rev7 submission 單一真實來源)
|
||||
**建議結論**:Major Revision
|
||||
**統計**:致命共識 ×3 · 嚴重 ×4 · 融合新增 ×5 · 改向/不採納 ×1 · 結構/Minor ×15
|
||||
|
||||
投票圖例:★ = 強烈標記 / ● = 提及 / ○ = 弱提及 / — = 未提。欄位 G=Gemini, C=ChatGPT, O=Opus。
|
||||
|
||||
執行順序:① F1 → ② F2+G1 → ③ F3、F7 與宣稱降溫 → ④ F4–F6 與 F5 穩健性套組 → ⑤ 結構與 Minor。
|
||||
|
||||
---
|
||||
|
||||
## Tier 1 — 致命(三審高共識,不修則中心宣稱垮)
|
||||
|
||||
- [x] **F1 — 校準 null 類別錯誤**(G★ C○ O★)|偏誤方向 anti-conservative ✅ 文字完成(abstract/§I/§III-D/§III-E/§IV-C/§V/§VI)
|
||||
- 已做:全文 ICCR→specificity proxy;撤回所有「139×/40–59×」式比較;§III-E 新增 between vs within 區分、明說 within-CPA 偽陽率不可估計、ICCR 連 bound 都不是、偏誤 anti-conservative。
|
||||
- ✅ benchmark 問題已 §III-E 主動防守:無 OUR-population 親簽標記、公開集屬不同族群/字體/管線→借入會重引跨分布假設(違 label-free 初衷),故報告限制非誤導 proxy。不做(用戶/領域判斷確認做不出)。
|
||||
- [x] **F2 — Firm 為被混淆的 treatment**(G★ C★ O★)✅ 完成(PDF 解析具名證據)
|
||||
- ✅ 880 份 PDF 管線審計:純掃描 2013 82% → 2021 崩到 1%;metadata 點名掃描機型(Fuji Xerox D125/ApeosPort)→ Table V 寫入 §V-B;量測基底本身在 2020/21 轉變 = 混淆具名鐵證。
|
||||
- ✅ byte-identical era split:232 數位年代(偵測性放大,已 hedge)vs 30 掃描年代(管線無關鐵證,18 在 Firm A)→ §V-B + §IV-C 交叉註記。
|
||||
- 腳本 pipeline_audit.py / pipeline_audit.csv 已存。
|
||||
- [x] **F3 — 吃重輸出零驗證**(G○ C★ O★)✅ 完成
|
||||
- 已做:刪除 byte-identical「sanity check / recall 下界」修辭→改 prevalence signal;headline 全面 screening/triage/prevalence 口徑(abstract/貢獻/結論皆已轉),通讀確認無 detection-verdict 過度宣稱。
|
||||
|
||||
## Tier 2 — 嚴重(雙審共識)
|
||||
|
||||
- [x] **F4 — held-out ≠ blinded**(G○ C★ O★)|Firm A 為已知 institutional positive case ✅ 完成
|
||||
- 已做:§IV-C 標題改「Held-Out Benchmark: Firm A (a Known Positive)」+ 新增 quasi-positive institutional benchmark 段;abstract/§II 資料切分/§VI 全部改「known-positive benchmark / not a blinded test」。
|
||||
- [x] **F5 — 旗標率 pool-size / 極值依賴**(G● C★ O●)|any-pair 爭議真正內核 ✅ 核心完成
|
||||
- ✅ 已做:pool-size 分層(Firm A 每一層都壓制 BCD:<50 66 vs 20%、…、400+ 82 vs 29%)→ pool size 無法解釋 firm gap;accountant-clustered bootstrap gap 53.7pp CI[49.5,57.5]。皆寫入 §IV-C。
|
||||
- ✅ 增補完成:firm+year FE logistic(控時間/管線後 B/C/D OR 0.116/0.061/0.070,仍低一個量級);leave-one-year-out gap 53.1–54.9pp(任一年剔除皆穩,含 2022–23)。寫入 §IV-C「Four further checks」;腳本 f5_fe_loyo.py。
|
||||
- [x] **F6 — clean reference exogeneity** ✅ 完成(文字+既有證據)
|
||||
- §III-E 新增:floor 為 conditional-on-correct-clean;污染只會抬高 floor → 對 Firm A 對比保守(known-safe 方向);leave-one-baseline-firm-out 不動 floor;crossover scope 0.8547→0.8302(≤0.025)。ICCR 在不同 clean-group 下重算需 canonical sampler,未擅自重做。
|
||||
- [x] **F7 — 宣稱範圍過大**(G● C★ O●)|detection / operational labels / tunable / 中文語料可直接採用 ✅ 完成
|
||||
- 已做:貢獻條列 operational labels→risk strata;全文 specificity→specificity proxy;中文語料「adopt directly」→「starting reference for comparable Chinese-signature pipelines, subject to recalibration」;tunable 見 G3。
|
||||
|
||||
## 仲裁(三審分歧,注意採納方向)
|
||||
|
||||
- [x] **A — 不 fine-tune 是 label-free 的必然** ✅ 完成
|
||||
- §II 末新增主動論證段:supervised metric learning 需 labelled pairs = 正是 archive 沒有的 ground truth;label-free 非弱化版而是唯一誠實選項;貢獻為方法論非架構;fine-tune 留待 protocol first-run 取得 labelled sample 後。
|
||||
- [x] **B — any-pair 嚴重性分歧** ✅ 併入 F5(已完成)
|
||||
|
||||
## 融合新增(三審皆漏,全採納)
|
||||
|
||||
- [x] **G1 — staggered e-signing adoption → event study** |**改為誠實描述+招認限制(用戶定案)** ✅ 完成
|
||||
- 硬發現:資料無乾淨 staggered adoption(A 全程高、C 2022 跳、B 2023 跳、D 緩升),跳升跨整年且集中審計季 → 內部時序無法分離 adoption vs 管線變動,且反推導入點會循環。
|
||||
- ✅ 已做:§V-B 升級為「Time Trend and the Firm–Pipeline Confound」,注入真實異質時序 + F2 指紋 + 明列 event study(需外部導入日期)為 future work;不杜撰日期。firm_year_hc_panel.csv 已存備圖。
|
||||
- [x] **G2 — 前處理壓縮 cosine 尺度** ✅ 完成(可驗證事實+construct,ablation 列 future)
|
||||
- §V-A 新增:cosine 97.7% ≥0.90、median 0.969、僅 0.3% <0.85;0.95 cut 坐落飽和區(~76% 在其上)→ cosine 單獨幾乎不分辨、靠 dHash;padding/normalization 完整量化需重跑 CNN ablation(DB 做不到)列 future。注意:融合表「95.2%」我配對驗證對不上,未引用。
|
||||
- [x] **G3 — 「tunable operating point」單向空心**(recall 不可觀測 → 無 P–R trade-off)✅ 完成
|
||||
- 已做:§III-D (i) + §V operating-point 改為「conservativeness dial, not a precision–recall control」;只能單向收緊、無可校準的 recall 取捨面;abstract 移除 operator-tunable 措辭。
|
||||
- [x] **G4 — byte-identical 跨案件/跨日期** ✅ 完成(DB 驗證)
|
||||
- 驗證:262 筆 pixel-identical 全部 match 到**不同 source_pdf**(0 同檔),170/262 跨月 → 排除重複申報/同報表雙計;§IV-C 補述。
|
||||
- [x] **G5 — 低率 ≠ 少數** ✅ 完成
|
||||
- ✅ 已做:§IV-A 補「888 期望巧合 HC flags(CI [677,1,098])= 0.59%×150,442」+「low rate ≠ small number、單一 HC flag 不單獨解讀」。
|
||||
|
||||
## 結構 / 格式 — Minor(含三審共識與細讀)
|
||||
|
||||
- [x] **M1** — 摘要重構 ✅;problem→method→data→finding→limitation 弧線,刪「This is not forgery」口語句,併入 F1/F4/F5/F7 誠實框架。
|
||||
- [x] **M2** — §I 首次出現處新增 operational definition ✅;明確區分 handwritten/seal/overlay/e-sign/proxy + 排除 cryptographic digital signature,準則=image reuse 可見結果。
|
||||
- [x] **M3** — §III-A 強化免責 ✅;訪談 self-reported/anonymized/不可重製,吻合=consistency with domain knowledge,非 accuracy/recall 量測。
|
||||
- [x] **M4** — Figure 3 換真實 2D density ✅;make_fig3_density.py 產 n=150,441 log-density + 五區疊加 + 軸刻度;caption 改為描述真實分布。
|
||||
- [x] **M5** — §X→Section X ✅(114 處全替換,無 malformed)。
|
||||
- [x] **M6** — specificity→specificity proxy ✅(隨 F1/F7 完成)。
|
||||
- [x] **M7** — Table II-b 後新增 reconciliation 段 ✅;直接解釋 within(21–29%) vs between(0.59%) 不同量,「clean」=between 巧合罕見非 within 低。
|
||||
- [x] **M8** — 新增 §V-D Threats to Validity ✅;8 條集中列出(含 bias 方向與交叉引用)。
|
||||
- [x] **M9** — §IV-B 新增框架句 ✅;分清 empirical(比率/巧合率/byte-id) vs designed procedure(4 moves);protocol first-run 明列 future work。
|
||||
- [x] **M10** — 對齊完成 ✅;168,755=matched、168,740=有測值(差 15=單簽會計師 pool=1,DB 驗證),§IV-A 補註;226=cell 全部、206=有足夠簽名子集,§V future-work 補「206 of 226」。
|
||||
- [x] **M11**(半-Major)— 語料範圍釐清 ✅
|
||||
- §III-B 新增一句:primary sample=Big-4;150,442=valid∩有兩測值(60,448/38,993/34,248/16,752);non-Big-4 僅入 §V-C crossover 穩健性、不入 calibration/headline。
|
||||
- [x] **M12** — 新增 Table II-c(A–D 2020–2023 五分類)✅;邏輯以 2013–2019 Firm B 重現 Table II-b 驗證通過。
|
||||
- [x] **M13** — 拼寫統一美式 ✅(behaviour/labelled/centring/colour→美式,30 處;references 內保留原拼寫)。⏳ placeholder 作者/機構/DOI/biography 投稿前補(double-blind,待你)。
|
||||
- [x] **M14** — 參考文獻體例 ✅(網路查證)
|
||||
- 查證結論:[4]SigNet(dblp=CoRR)、[8]Brimoh、[9]Woodruff、[24]Qwen2.5-VL **皆無正式刊出版本,維持 arXiv 即正確**(reviewer 誤判有正式版)。[25] 補官方 docs URL;[27] 升級為精確永久連結 + 日期(Jan 21, 2013)。
|
||||
- [x] **M15** — 新增 Table I-a 縮寫/門檻對照表 ✅(HC/MC/HSC/UN/LH + cuts + ICCR/c/d 記號)。⏳ II-b vs IV 整併為主觀排版判斷,建議你決定。
|
||||
|
||||
---
|
||||
|
||||
## 分類:可立即文字改 vs 需新分析/資料 vs 需 co-author 決策
|
||||
|
||||
**A. 純文字重構(無需新數據,可現在做)**:F1(術語/撤回 139×)、F4、F7、G3、G4(若資料已知)、M1、M2、M3、M5、M6、M8、M9、M13、M15、A(主動論證段落)
|
||||
|
||||
**B. 需新分析 / 跑資料**:F2(firm-metadata 抽取)、F3(prevalence 數字)、F5(subsampling + bootstrap + FE + LOYO 套組)、F6(clean-group 敏感度)、G1(event study)、G2(前處理量化)、G5(絕對期望數)、M4(真實 density 圖)、M7(B/C/D HC 解釋需查數)、M11(語料分母核對)、M12(五分類表)
|
||||
|
||||
**C. 需 co-author(Jimmy)決策 / 確認**:是否補 within-CPA 親簽 benchmark(F1 理想項)、G1 event study 範圍、最終宣稱降溫幅度
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
<!-- IEEE Access target: <= 250 words, single paragraph -->
|
||||
|
||||
Regulations require Certified Public Accountants (CPAs) to attest each audit report with a signature, but digitization makes reusing a stored signature image across reports — through administrative stamping or firm-level electronic signing — undermining individualized attestation. We build an end-to-end pipeline detecting such *non-hand-signed* signatures at scale: a Vision-Language Model identifies signature pages, YOLOv11 localizes signatures, ResNet-50 supplies deep features, and a dual-descriptor layer combines cosine similarity with an independent-minimum perceptual hash (dHash) to separate *style consistency* from *image reproduction*. Applied to 90,282 Taiwan audit reports (2013–2023), the pipeline yields 182,328 signatures from 758 CPAs; primary analyses are scoped to the Big-4 sub-corpus (437 CPAs; 150,442 signatures). Distributional diagnostics show that the apparent multimodality of the descriptor distribution dissolves under joint firm-mean centring and integer-tie jitter ($p$ rises to $0.35$), so no within-population bimodal antimode anchors the operational thresholds. We instead adopt an anchor-based inter-CPA coincidence-rate (ICCR) calibration at three units: per-comparison ($0.0006$ at cos$>0.95$; $0.0013$ at dHash$\leq 5$; $0.00014$ jointly), pool-normalised per-signature ($0.11$ under the deployed any-pair high-confidence rule), and per-document ($0.34$ for the operational HC+MC alarm). Firm heterogeneity is decisive: Firm A's per-document HC+MC alarm rate is $0.62$ versus $0.09$–$0.16$ at Firms B/C/D after pool-size adjustment, and under the deployed any-pair rule $77$–$99\%$ of inter-CPA collisions concentrate within the source firm — consistent with firm-level template-like reuse. We position the system as a specificity-proxy-anchored screening framework with human-in-the-loop review, not as a validated forensic detector; no calibrated error rates are reportable without signature-level ground truth.
|
||||
Regulations require Certified Public Accountants (CPAs) to attest each audit report with a signature, but digitization makes it feasible to reuse a stored signature image across reports — through administrative stamping or firm-level electronic signing — thereby undermining individualized attestation. We build an end-to-end pipeline for screening such *non-hand-signed* signatures at scale: a Vision-Language Model identifies signature pages, YOLOv11 localizes signatures, ResNet-50 supplies deep features, and a dual-descriptor layer combines cosine similarity with an independent-minimum perceptual hash (dHash) to separate *style consistency* from *image reproduction*. Applied to 90,282 Taiwan audit reports (2013–2023), the pipeline yields 182,328 signatures from 758 CPAs; primary analyses are scoped to the Big-4 sub-corpus (437 CPAs; 150,442 signatures). Distributional diagnostics show that the apparent multimodality of the descriptor distribution dissolves under joint firm-mean centring and integer-tie jitter ($p$ rises to $0.35$), so no within-population bimodal antimode anchors the operational thresholds. We instead adopt an anchor-based inter-CPA coincidence-rate (ICCR) calibration at three units: per-comparison ($0.0006$ at cos$>0.95$; $0.0013$ at dHash$\leq 5$; $0.00014$ jointly), pool-normalised per-signature ($0.11$ under the deployed any-pair high-confidence rule), and per-document ($0.34$ for the operational HC+MC alarm). The framework surfaces pronounced firm-level heterogeneity: Firm A's per-document HC+MC ICCR is $0.62$ versus $0.09$–$0.16$ at Firms B/C/D (gap persists after pool-size adjustment), and $77$–$99\%$ of inter-CPA collisions concentrate within the source firm. This contrast is consistent with firm-level template-like reuse but not independently diagnostic, since descriptor-only data cannot separate reuse from digitisation-pipeline or signing-style homogeneity within a firm; we report it as a scope limitation rather than a mechanism finding. We position the system as a specificity-proxy-anchored screening framework with human-in-the-loop review, not as a validated forensic detector; no calibrated error rates are reportable without signature-level ground truth.
|
||||
|
||||
<!-- Word count: 247 -->
|
||||
<!-- Word count: 281 -->
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
# Appendix A. BD/McCrary Bin-Width Sensitivity (Signature Level)
|
||||
# Appendix A. Supplementary Diagnostic Detail
|
||||
|
||||
The main text (Section III-I, Section IV-D.2) treats the Burgstahler-Dichev / McCrary discontinuity procedure [38], [39] as a *density-smoothness diagnostic* rather than as a threshold estimator.
|
||||
This appendix documents the empirical basis for that framing by sweeping the bin width across four (variant, bin-width) panels: Firm A and full-sample, each in the cosine and $\text{dHash}_\text{indep}$ direction.
|
||||
## A.1. BD/McCrary Bin-Width Sensitivity (Signature Level)
|
||||
|
||||
The main text (Section III-I, Section IV-D Table VI) treats the Burgstahler-Dichev / McCrary discontinuity procedure [38], [39] as a *density-smoothness diagnostic* rather than as a threshold estimator.
|
||||
This subsection documents the empirical basis for that framing by sweeping the bin width across four (variant, bin-width) panels: Firm A and full-sample, each in the cosine and $\text{dHash}_\text{indep}$ direction.
|
||||
|
||||
**Table A.I.** BD/McCrary Bin-Width Sensitivity (two-sided $\alpha = 0.05$, $|Z| > 1.96$).
|
||||
|
||||
<!-- TABLE A.I: BD/McCrary Bin-Width Sensitivity (two-sided alpha = 0.05, |Z| > 1.96)
|
||||
| Variant | n | Bin width | Best transition | z_below | z_above |
|
||||
|---------|---|-----------|-----------------|---------|---------|
|
||||
| Firm A cosine (sig-level) | 60,448 | 0.003 | 0.9870 | -2.81 | +9.42 |
|
||||
@@ -20,45 +23,38 @@ This appendix documents the empirical basis for that framing by sweeping the bin
|
||||
| Full-sample dHash_indep (sig-l.) | 168,740 | 1 | 2.0 | -6.22 | +4.89 |
|
||||
| Full-sample dHash_indep (sig-l.) | 168,740 | 2 | 10.0 | -7.35 | +3.83 |
|
||||
| Full-sample dHash_indep (sig-l.) | 168,740 | 3 | 9.0 | -11.05 | +45.39 |
|
||||
-->
|
||||
|
||||
Two patterns are visible in Table A.I.
|
||||
First, the procedure consistently identifies a "transition" under every bin width, but the *location* of that transition drifts monotonically with bin width (Firm A cosine: 0.987 → 0.985 → 0.980 → 0.975 as bin width grows from 0.003 to 0.015; full-sample dHash: 2 → 10 → 9 as the bin width grows from 1 to 3).
|
||||
The $Z$ statistics also inflate superlinearly with the bin width (Firm A cosine $|Z|$ rises from $\sim 9$ at bin 0.003 to $\sim 106$ at bin 0.015) because wider bins aggregate more mass per bin and therefore shrink the per-bin standard error on a very large sample.
|
||||
Both features are characteristic of a histogram-resolution artifact rather than of a genuine density discontinuity.
|
||||
|
||||
Second, the candidate transitions all locate *inside* the non-hand-signed mode (cosine $\geq 0.975$, dHash $\leq 10$) rather than between modes, which is the location pattern we would expect of a clean two-mechanism boundary.
|
||||
Second, the candidate transitions all locate *inside* the high-similarity region (cosine $\geq 0.975$, dHash $\leq 10$) rather than at a between-mode boundary, which is the location pattern we would expect of a clean within-population antimode.
|
||||
|
||||
Taken together, Table A.I shows that the signature-level BD/McCrary transitions are not a threshold in the usual sense---they are histogram-resolution-dependent local density anomalies located *inside* the non-hand-signed mode rather than between modes.
|
||||
This observation supports the main-text decision to use BD/McCrary as a density-smoothness diagnostic rather than as a threshold estimator and reinforces the joint reading of Section IV-D that per-signature similarity does not form a clean two-mechanism mixture.
|
||||
This observation supports the main-text decision to use BD/McCrary as a density-smoothness diagnostic rather than as a threshold estimator and reinforces the joint reading of Section IV-D that the descriptor distributions do not contain a within-population bimodal antimode that could anchor an operational threshold.
|
||||
|
||||
Raw per-bin $Z$ sequences and $p$-values for every (variant, bin-width) panel are available in the supplementary materials.
|
||||
|
||||
# Appendix B. Table-to-Script Provenance
|
||||
## A.2. Diagnostic Summary
|
||||
|
||||
For reproducibility, the following table maps each numerical table in Section IV to the analysis script that produces its underlying values and to the report file emitted by that script. Scripts are under `signature_analysis/`. Report artifact paths below are listed relative to the project's analysis report root, which is `/Volumes/NV2/PDF-Processing/signature-analysis/` in our local deployment; replicators should rebase the paths to whatever report root they configure when invoking the scripts.
|
||||
Section III-M positions the unsupervised-diagnostic strategy as a set of complementary checks, each addressing one specific failure mode of an unsupervised screening classifier with an explicitly disclosed untested assumption. Table A.II maps each diagnostic to the failure mode it addresses and to the untested assumption it relies on.
|
||||
|
||||
<!-- TABLE B.I: Manuscript table → reproduction artifact
|
||||
| Manuscript table | Generating script | Report artifact |
|
||||
|------------------|-------------------|-----------------|
|
||||
| Table III (extraction results) | `02_extract_features.py`; `09_pdf_signature_verdict.py` | `reports/extraction_methodology.md`; `reports/pdf_signature_verdicts.json` |
|
||||
| Table IV (intra/inter all-pairs cosine statistics) | `10_formal_statistical_analysis.py` | `reports/formal_statistical_data.json`; `reports/formal_statistical_report.md` |
|
||||
| Table V (Hartigan dip test) | `15_hartigan_dip_test.py` | `reports/dip_test/dip_test_results.json` |
|
||||
| Table VI (signature-level threshold-estimator summary) | `17_beta_mixture_em.py`; `25_bd_mccrary_sensitivity.py` | `reports/beta_mixture/beta_mixture_results.json`; `reports/bd_sensitivity/bd_sensitivity.json` |
|
||||
| Table IX (Firm A whole-sample capture rates) | `19_pixel_identity_validation.py`; `24_validation_recalibration.py` | `reports/pixel_validation/pixel_validation_results.json`; `reports/validation_recalibration/validation_recalibration.json` |
|
||||
| Table X (cosine threshold sweep, FAR vs inter-CPA negatives) | `21_expanded_validation.py` | `reports/expanded_validation/expanded_validation_results.json` |
|
||||
| Table XI (held-out vs calibration Firm A capture rates) | `24_validation_recalibration.py` | `reports/validation_recalibration/validation_recalibration.json` |
|
||||
| Table XII (operational-cut sensitivity 0.95 vs 0.945) | `24_validation_recalibration.py` | `reports/validation_recalibration/validation_recalibration.json` |
|
||||
| Table XII-B (cosine-threshold tradeoff: capture vs inter-CPA FAR) | `21_expanded_validation.py` (FAR column; canonical 50k-pair anchor); inline computation in revision (Firm A and non-Firm-A capture columns) | `reports/expanded_validation/expanded_validation_results.json` |
|
||||
| Table XIII (Firm A per-year cosine distribution) | `29_firm_a_yearly_distribution.py` | `reports/firm_a_yearly/firm_a_yearly_distribution.json` |
|
||||
| Fig. 4 (per-firm yearly best-match cosine, 2013-2023) | `30_yearly_big4_comparison.py` | `reports/figures/fig_yearly_big4_comparison.{png,pdf}`; `reports/firm_yearly_comparison/firm_yearly_comparison.{json,md}` |
|
||||
| Tables XIV / XV (partner-level similarity ranking) | `22_partner_ranking.py` | `reports/partner_ranking/partner_ranking_results.json` |
|
||||
| Table XVI (intra-report classification agreement) | `23_intra_report_consistency.py` | `reports/intra_report/intra_report_results.json` |
|
||||
| Table XVII (document-level five-way classification) | `09_pdf_signature_verdict.py`; `12_generate_pdf_level_report.py` | `reports/pdf_signature_verdicts.json`; `reports/pdf_signature_verdict_report.md` (CSV / XLSX bulk reports also at `reports/`) |
|
||||
| Table XVIII (backbone ablation) | `paper/ablation_backbone_comparison.py` | `ablation/ablation_results.json` (sibling of `reports/`) |
|
||||
| Table A.I (BD/McCrary bin-width sensitivity) | `25_bd_mccrary_sensitivity.py` | `reports/bd_sensitivity/bd_sensitivity.json` |
|
||||
| Byte-identity decomposition (145 / 50 / 180 / 35; Section IV-F.1) | `28_byte_identity_decomposition.py` | `reports/byte_identity_decomp/byte_identity_decomposition.json` |
|
||||
| Cross-firm dual-descriptor convergence (Section IV-H.2) | `28_byte_identity_decomposition.py` | `reports/byte_identity_decomp/byte_identity_decomposition.json` |
|
||||
-->
|
||||
**Table A.II.** Diagnostics, failure mode addressed, and disclosed untested assumption.
|
||||
|
||||
The table-to-script mapping above is intended as a navigation aid for replicators. All scripts run deterministically under the fixed random seeds documented in the supplementary materials; the artifact paths above were verified against the local deployment at the time of submission, and any reviewer reproduction step should re-emit the artifacts from the listed scripts rather than depend on the absolute path layout.
|
||||
| Diagnostic | Failure mode addressed | Disclosed untested assumption |
|
||||
|---|---|---|
|
||||
| Composition decomposition (§III-I.4; Scripts 39b–39e) | Whether descriptor multimodality is within-population (mechanism) or between-group (composition + integer artefact); $p_{\text{median}} = 0.35$ under joint firm-mean centring + integer-tie jitter | Integer-tie jitter and firm-mean centring are unbiased over the descriptor support; corroborated by Big-4 per-firm jitter (Script 39d; per-firm dHash rejection disappears under jitter at every Big-4 firm) and Big-4 pooled centred + jittered ($n_{\text{seeds}} = 5$; Script 39e) |
|
||||
| Per-comparison inter-CPA coincidence rate (§III-L.1; Script 40b) | Pair-level specificity proxy under a random-pair negative anchor | Inter-CPA pairs are negative (i.e., not template-related); partially violated by within-firm sharing (§III-L.4) |
|
||||
| Pool-normalised per-signature ICCR (§III-L.2; Script 43) | Deployed-rule specificity proxy at per-signature unit, accounting for pool size | Same as above + that pool replacement preserves the negative-anchor property |
|
||||
| Document-level ICCR (§III-L.3; Script 45) | Operational alarm rate proxy at per-document unit under three alarm definitions | Same as above |
|
||||
| Firm-heterogeneity logistic regression (§III-L.4; Script 44) | Multiplicative effect of firm membership on per-signature rate, controlling for pool size | Per-signature observations are clustered by CPA/firm; naïve standard errors unreliable; cluster-robust analysis is a future check |
|
||||
| Cross-firm hit matrix (§III-L.4; Script 44) | Concentration of inter-CPA collisions within source firm | Concentration depends on deployed-rule semantics (the stricter same-pair joint event yields $97.0$–$99.96\%$ within-firm at all four firms versus $76.7$–$98.8\%$ under any-pair; §III-L.4); per-document per-firm assignment uses Script 45's mode-of-firms tie-break (§IV-M.4) |
|
||||
| Alert-rate sensitivity sweep (§III-L.5; Script 46) | Local sensitivity of deployed rule to threshold perturbation | Gradient comparison is descriptive, not a formal plateau test |
|
||||
| Convergent score Spearman ranking (§III-K.1; Script 38) | Internal-consistency of three feature-derived per-CPA scores | Scores share underlying inputs and are not statistically independent |
|
||||
| Pixel-identical conservative positive capture (§III-K.4; Script 40) | Trivial sanity check on the conservative positive anchor | Anchor is tautologically captured by any reasonable threshold |
|
||||
| LOOO firm-level reproducibility (§III-K.3; Scripts 36, 37) | Algorithmic stability of K=2 / K=3 partition across firm folds | Stability is necessary but not sufficient for classification validity |
|
||||
|
||||
# Appendix B. Reproducibility Materials
|
||||
|
||||
The full table-to-script provenance mapping, script source code, and report artefacts for every numerical table and figure in this paper are provided in the supplementary materials. Scripts run deterministically under fixed random seeds documented there; reviewer reproduction should re-emit artefacts from the listed scripts rather than rely on any local path layout.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# VI. Conclusion and Future Work
|
||||
|
||||
We present a fully automated pipeline for detecting non-hand-signed CPA signatures in Taiwan-listed financial audit reports and a multi-tool framework for characterising and disclosing its operational behaviour at the Big-4 sub-corpus scope. The pipeline processes raw PDFs through VLM-based page identification, YOLO-based signature detection, ResNet-50 feature extraction, and dual-descriptor (cosine + independent-minimum dHash) similarity computation. The operational output is an inherited Paper A five-way per-signature classifier with worst-case document-level aggregation (§III-L). Applied to 90,282 audit reports filed between 2013 and 2023, the pipeline extracts 182,328 signatures from 758 CPAs, with the Big-4 sub-corpus (437 CPAs at accountant level; 150,442–150,453 signatures at signature level) as the primary analytical population.
|
||||
We present a fully automated pipeline for screening non-hand-signed CPA signatures in Taiwan-listed financial audit reports, together with an anchor-calibrated screening framework that characterises the pipeline's operational behaviour at the Big-4 sub-corpus scope under explicit unsupervised assumptions. The pipeline processes raw PDFs through VLM-based page identification, YOLO-based signature detection, ResNet-50 feature extraction, and dual-descriptor (cosine + independent-minimum dHash) similarity computation. The operational output is the deployed five-way per-signature classifier with worst-case document-level aggregation (§III-H.1; calibrated in §III-L). Applied to 90,282 audit reports filed between 2013 and 2023, the pipeline extracts 182,328 signatures from 758 CPAs, with the Big-4 sub-corpus (437 CPAs at accountant level; 150,442–150,453 signatures at signature level) as the primary analytical population.
|
||||
|
||||
Our central methodological contributions are: (1) a composition decomposition (Scripts 39b–39e) that establishes the absence of a within-population bimodal antimode in the Big-4 descriptor distribution: the apparent multimodality dissolves under joint firm-mean centring and integer-tie jitter ($p_{\text{median}} = 0.35$), so distributional "natural-threshold" framings of the inherited operating points are not empirically supported; (2) an anchor-based inter-CPA coincidence-rate (ICCR) calibration at three units of analysis — per-comparison ($0.0006$ at cos$>0.95$; $0.0013$ at dHash$\leq 5$; $0.00014$ jointly), pool-normalised per-signature ($0.11$ for the deployed any-pair HC rule), and per-document ($0.34$ for the operational HC$+$MC alarm) — with explicit terminological replacement of "FAR" by "ICCR" given the unsupervised setting; (3) firm heterogeneity quantification: logistic regression with pool-size adjustment gives odds ratios $0.053$, $0.010$, $0.027$ for Firms B/C/D relative to Firm A reference, indicating a large multiplicative effect that pool-size differences do not explain; (4) cross-firm hit matrix evidence that under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms), consistent with firm-specific template, stamp, or document-production reuse mechanisms; (5) K=3 mixture demoted from "three mechanism clusters" to a descriptive firm-compositional partition; (6) three feature-derived scores converging on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$, reported as internal consistency rather than external validation; (7) $0\%$ positive-anchor miss rate on 262 byte-identical Big-4 signatures with the conservative-subset caveat; and (8) a ten-tool unsupervised-validation collection (§III-M Table XXVII) that explicitly discloses each tool's untested assumption and positions the system as an anchor-calibrated screening framework with human-in-the-loop review, not as a validated forensic detector.
|
||||
Our central methodological contributions are: (1) a composition decomposition that establishes the absence of a within-population bimodal antimode in the Big-4 descriptor distribution: the apparent multimodality dissolves under joint firm-mean centring and integer-tie jitter ($p_{\text{median}} = 0.35$), so distributional "natural-threshold" framings of the deployed operating points are not empirically supported; (2) an anchor-based inter-CPA coincidence-rate (ICCR) calibration at three units of analysis — per-comparison ($0.0006$ at cos$>0.95$; $0.0013$ at dHash$\leq 5$; $0.00014$ jointly), pool-normalised per-signature ($0.11$ for the deployed any-pair HC rule), and per-document ($0.34$ for the operational HC$+$MC alarm) — with explicit terminological replacement of "FAR" by "ICCR" given the unsupervised setting; (3) firm-level heterogeneity surfaced by the framework: logistic regression with pool-size adjustment gives odds ratios $0.053$, $0.010$, $0.027$ for Firms B/C/D relative to Firm A reference, a contrast that pool-size differences do not explain and that we report as a framework-discriminative observation rather than a mechanism finding (§V-H); (4) cross-firm hit matrix evidence that under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms), consistent with — but not independently establishing — firm-level template-like reuse, digitisation-pipeline homogeneity, or signing-style similarity, which descriptor-only data cannot separate (§V-H); (5) K=3 mixture demoted from "three mechanism clusters" to a descriptive firm-compositional partition; (6) three feature-derived scores converging on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$, reported as internal consistency rather than external validation; (7) $0\%$ positive-anchor miss rate on 262 byte-identical Big-4 signatures with the conservative-subset caveat; and (8) explicit disclosure of each diagnostic's untested assumption (Appendix A Table A.II), positioning the system as an anchor-calibrated screening framework with human-in-the-loop review rather than as a validated forensic detector.
|
||||
|
||||
Future work falls in four directions. *First*, a small-scale human-rated validation set would enable direct ROC optimisation and provide signature-level ground truth that v4.0 fundamentally lacks; without such ground truth, no true error rates can be reported. *Second*, the within-firm collision concentration documented in §III-L.4 (any-pair $76.7$–$98.8\%$ across Big-4; same-pair joint $97.0$–$99.96\%$) invites a separate study to distinguish deliberate template sharing from passive firm-level production artefacts (shared scanners, common form templates, identical report-generation infrastructure) — a question the inter-CPA-anchor analysis alone cannot resolve. *Third*, the descriptive Firm A versus Firms B/C/D contrast (per-document HC$+$MC alarm $0.62$ vs $0.09$–$0.16$) — together with v3.x's byte-level evidence of 145 pixel-identical signatures across $\sim 50$ distinct Firm A partners — invites a companion analysis examining whether such firm-level signing patterns correlate with established audit-quality measures. *Fourth*, generalisation to mid- and small-firm contexts requires extending the anchor-based ICCR framework to scopes where firm-level LOOO folds are not available; the §III-I.4 composition diagnostics already document that the absence of within-population bimodality is corpus-universal, so the v4.0 calibration approach in principle generalises, but a full extension with cluster-robust uncertainty quantification is left as future work.
|
||||
Future work falls in four directions. *First*, a small-scale human-rated labelled set would enable direct ROC optimisation and provide the signature-level ground truth that the present analysis fundamentally lacks; without such ground truth, no true error rates can be reported. *Second*, the within-firm collision concentration documented in §III-L.4 (any-pair $76.7$–$98.8\%$ across Big-4; same-pair joint $97.0$–$99.96\%$) invites a separate study to distinguish deliberate template sharing from passive firm-level production artefacts (shared scanners, common form templates, identical report-generation infrastructure) — a question the inter-CPA-anchor analysis alone cannot resolve. *Third*, the descriptive Firm A versus Firms B/C/D contrast (per-document HC$+$MC alarm $0.62$ vs $0.09$–$0.16$) — together with the byte-level evidence of 145 pixel-identical signatures across $\sim 50$ distinct Firm A partners — invites a companion analysis examining whether such firm-level signing patterns correlate with established audit-quality measures. *Fourth*, generalisation to mid- and small-firm contexts requires extending the anchor-based ICCR framework to scopes where firm-level LOOO folds are not available; the §III-I.4 composition diagnostics already document that the absence of within-population bimodality holds across the tested eligible scopes, so the calibration approach in principle generalises, but a full extension with cluster-robust uncertainty quantification is left as future work.
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
|
||||
**Data availability.** All audit reports analysed in this study were obtained from the Market Observation Post System (MOPS) operated by the Taiwan Stock Exchange Corporation, a publicly accessible regulatory disclosure platform. The CPA registry used to map signatures to certifying CPAs is publicly available. Signature images, model weights, and reproducibility scripts are available in the supplementary materials.
|
||||
|
||||
**Funding.** [To be filled in before submission.]
|
||||
<!-- Funding statement to be inserted before submission:
|
||||
**Funding.** [acknowledge any grants, awards, or institutional support here]
|
||||
-->
|
||||
|
||||
@@ -6,64 +6,72 @@ Non-hand-signing differs from forgery in that the questioned signature is produc
|
||||
|
||||
## B. Per-Signature Similarity is a Continuous Quality Spectrum; the Accountant-Level Multimodality is Composition-Driven
|
||||
|
||||
A central empirical finding of v3.x was that *per-signature* similarity does not admit a clean two-mechanism mixture: dip-test fails to reject unimodality at the signature level for Firm A, BIC prefers a 3-component fit, and BD/McCrary candidate transitions lie inside the high-similarity mode rather than between modes. v4.0 strengthens and extends this signature-level reading.
|
||||
|
||||
The Big-4 accountant-level descriptor distribution does reject unimodality on both marginals at $p < 5 \times 10^{-4}$ (Script 34). v4.0's composition decomposition (§III-I.4; Scripts 39b–39e) shows that this rejection is fully attributable to two non-mechanistic sources: (a) between-firm location-shift effects on both axes — Firm A's mean dHash of $2.73$ versus Firms B/C/D's $6.46$, $7.39$, $7.21$ creates a multi-peaked pooled distribution that any single firm's distribution lacks — and (b) integer mass-point artefacts on the integer-valued dHash axis, which inflate the dip statistic against a continuous-density null. A 2×2 factorial diagnostic applied to the Big-4 pooled dHash (firm-mean centring × uniform integer jitter $[-0.5, +0.5]$, 5 jitter seeds) shows that the dip test fails to reject ($p_{\text{median}} = 0.35$, 0/5 seeds reject) when *both* corrections are applied; either correction alone leaves the rejection in place. Within-firm signature-level cosine and jittered-dHash dip tests fail to reject in every individual Big-4 firm and in every individual non-Big-4 firm with $\geq 500$ signatures tested (cosine: Scripts 39b/39c; jittered-dHash: Script 39d for Big-4 plus codex-verified read-only spike for the ten non-Big-4 firms; see §III-I.4). The descriptor distributions therefore lack a within-population bimodal antimode that could anchor an operational threshold. The K=2 / K=3 mixture fits are retained in §III-J as descriptive partitions of the joint Big-4 distribution that reflect firm-compositional structure, not as inferential evidence for two or three latent mechanism modes.
|
||||
The Big-4 accountant-level descriptor distribution rejects unimodality on both marginals at $p < 5 \times 10^{-4}$ (§IV-D Table V). The composition decomposition of §III-I.4 shows that this rejection is fully attributable to two non-mechanistic sources: (a) between-firm location-shift effects on both axes — Firm A's mean dHash of $2.73$ versus Firms B/C/D's $6.46$, $7.39$, $7.21$ creates a multi-peaked pooled distribution that any single firm's distribution lacks — and (b) integer mass-point artefacts on the integer-valued dHash axis, which inflate the dip statistic against a continuous-density null. A 2×2 factorial diagnostic applied to the Big-4 pooled dHash (firm-mean centring × uniform integer jitter $[-0.5, +0.5]$, 5 jitter seeds) shows that the dip test fails to reject ($p_{\text{median}} = 0.35$, 0/5 seeds reject) when *both* corrections are applied; either correction alone leaves the rejection in place. Within the Big-4 firms, the descriptor marginals at the signature level are unimodal once integer ties are broken (Scripts 39b, 39d); eligible non-Big-4 firms provide corroborating raw-axis evidence on the cosine dimension (Script 39c) but are not used as calibration evidence (§III-I.4). The descriptor distributions therefore lack a within-population bimodal antimode that could anchor an operational threshold. The K=2 / K=3 mixture fits are retained in §III-J as descriptive partitions of the joint Big-4 distribution that reflect firm-compositional structure, not as inferential evidence for two or three latent mechanism modes.
|
||||
|
||||
## C. Firm A as the Templated End of Big-4 (Case Study, Not Calibration Anchor)
|
||||
|
||||
Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the Big-4 descriptor plane. In the Big-4 K=3 hard-posterior assignment (now interpreted as a firm-compositional position assignment; §III-J), Firm A accounts for $0\%$ of C1 (low-cos / high-dHash position) and $82.5\%$ of C3 (high-cos / low-dHash position); the opposite pattern holds at Firm C, which has the highest C1 concentration at $23.5\%$. Firm A also accounts for 145 of the 262 byte-identical signatures in the Big-4 byte-identical anchor of §IV-H (with Firm B 8, Firm C 107, Firm D 2). The additional v3.x finding that the 145 Firm A pixel-identical signatures span 50 distinct Firm A partners (of 180 registered), with 35 byte-identical matches across different fiscal years, is inherited from v3.20.0 §IV-F.1 / Script 28 / Appendix B byte-decomposition output and was not regenerated in v4.0's spike scripts; we retain those numbers by reference.
|
||||
Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the Big-4 descriptor plane. In the Big-4 K=3 hard-posterior assignment (now interpreted as a firm-compositional position assignment; §III-J), Firm A accounts for $0\%$ of C1 (low-cos / high-dHash position) and $82.5\%$ of C3 (high-cos / low-dHash position); the opposite pattern holds at Firm C, which has the highest C1 concentration at $23.5\%$. Firm A also accounts for 145 of the 262 byte-identical signatures in the Big-4 byte-identical anchor of §IV-H (with Firm B 8, Firm C 107, Firm D 2). Byte-level decomposition of the 145 Firm A pixel-identical signatures (see supplementary materials) shows they span 50 distinct Firm A partners (of 180 registered), with 35 byte-identical matches occurring across different fiscal years.
|
||||
|
||||
In v4.0 we treat Firm A as a *templated-end case study* rather than as the calibration anchor for the operational threshold. Firm A enters the Big-4 anchor-based ICCR calibration on equal footing with the other three Big-4 firms (§III-L). The cross-firm hit matrix of §III-L.4 strengthens this framing: under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms). Firm A's high per-document HC$+$MC alarm rate of $0.62$ (versus Firms B/C/D's $0.09$–$0.16$) reflects high inter-CPA collision concentration under the deployed rule on real same-CPA pools, consistent with firm-specific template, stamp, or document-production reuse — though the inter-CPA-anchor analysis alone is not diagnostic of deliberate template sharing. The byte-level evidence of v3.x §IV-F.1 (Firm A's 145 pixel-identical signatures across $\sim 50$ distinct partners) provides direct evidence that firm-level template reuse does occur at Firm A; the within-firm collision pattern at all four Big-4 firms is consistent with that mechanism extending in milder form to Firms B/C/D.
|
||||
We treat Firm A as a *templated-end case study within the Big-4 sub-corpus* rather than as the calibration anchor for the operational threshold. Firm A enters the Big-4 anchor-based ICCR calibration on equal footing with the other three Big-4 firms (§III-L). The cross-firm hit matrix of §III-L.4 strengthens this framing: under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms). Firm A's per-document D2 inter-CPA proxy ICCR of $0.6201$ (versus Firms B/C/D's $0.09$–$0.16$) — the counterfactual rate at which Firm A documents would fire HC$+$MC if same-CPA pools were replaced by random inter-CPA candidates — reflects high inter-CPA collision concentration under the deployed rule, consistent with firm-specific template, stamp, or document-production reuse. (The corresponding observed rate on real same-CPA pools, from Table XVI, is substantially higher: $97.5\%$ HC$+$MC for Firm A; the proxy and observed rates measure different quantities and are not directly comparable.) The inter-CPA-anchor analysis alone is not diagnostic of deliberate template sharing. The byte-level evidence above (Firm A's 145 pixel-identical signatures across $\sim 50$ distinct partners) provides direct evidence of image-level reuse among Firm A signatures; the distribution across many partners is consistent with a firm-level template or production workflow, and the within-firm collision pattern at all four Big-4 firms is consistent with similar, milder within-firm collision patterns at Firms B/C/D, whose mechanisms may include template-like reuse, digitisation-pipeline homogeneity, or signing-style homogeneity (§V-H).
|
||||
|
||||
## D. K=2 / K=3 as Descriptive Firm-Compositional Partitions
|
||||
|
||||
Leave-one-firm-out cross-validation of the Big-4 mixture fit reveals a sharp contrast between K=2 and K=3 behaviour. K=2 is unstable: across-fold cosine-crossing deviation is $0.028$, and holding Firm A out gives a fold rule (cos $> 0.938$, dHash $\leq 8.79$) that classifies $100\%$ of held-out Firm A in the upper component, while holding any non-Firm-A Big-4 firm out gives a fold rule near (cos $> 0.975$, dHash $\leq 3.76$) that classifies $0\%$ of the held-out firm in the upper component. The K=2 boundary is essentially a Firm-A-vs-others separator — direct evidence that the K=2 partition reflects firm-compositional rather than mechanistic structure.
|
||||
|
||||
K=3 in contrast has a *reproducible component shape* at the descriptor-position level: across the four folds the C1 (low-cos / high-dHash) component cosine mean varies by at most $0.005$, the dHash mean by at most $0.96$, and the weight by at most $0.023$. Hard-posterior membership for the held-out firm is composition-sensitive (absolute differences $1.8$–$12.8$ pp across folds). Together with the §III-I.4 composition decomposition (no within-population bimodal antimode), the K=3 stability supports a descriptive reading: the Big-4 descriptor plane has a reproducible three-region partition that reflects how firm-compositional weight is distributed across the descriptor space, *not* a three-mechanism latent-class structure. We accordingly do not use K=3 hard-posterior membership as an operational classifier; we use it as the accountant-level descriptive summary that complements the deployed signature-level five-way classifier of §III-L.
|
||||
K=3 in contrast has a *reproducible component shape* at the descriptor-position level: across the four folds the C1 (low-cos / high-dHash) component cosine mean varies by at most $0.005$, the dHash mean by at most $0.96$, and the weight by at most $0.023$. Hard-posterior membership for the held-out firm is composition-sensitive (absolute differences $1.8$–$12.8$ pp across folds). Together with the §III-I.4 composition decomposition (no within-population bimodal antimode), the K=3 stability supports a descriptive reading: the Big-4 descriptor plane has a reproducible three-region partition that reflects how firm-compositional weight is distributed across the descriptor space, *not* a three-mechanism latent-class structure. We accordingly do not use K=3 hard-posterior membership as an operational classifier; we use it as the accountant-level descriptive summary that complements the deployed signature-level five-way classifier of §III-H.1.
|
||||
|
||||
## E. Three-Score Convergent Internal-Consistency
|
||||
|
||||
Three feature-derived scores agree on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$: the K=3 mixture posterior (a firm-compositional position score, not a mechanism cluster posterior); the reverse-anchor cosine percentile under a non-Big-4 reference distribution; and the inherited Paper A box-rule less-replication-dominated rate. The three scores are *not* statistically independent measurements — they are deterministic functions of the same per-CPA descriptor pair — so the convergence is documented as internal consistency rather than external validation against an independent ground truth (which the corpus does not provide for the hand-signed class). The strength of the convergence (all pairwise $|\rho| > 0.87$) and its persistence at the signature level (Cohen $\kappa = 0.87$ between per-CPA-fit and per-signature-fit K=3 binary labels) are nevertheless informative: per-CPA aggregation does not collapse the broad three-region ordering, and three different summarisations of the descriptor space produce broadly concordant per-CPA rankings, with a residual non-Firm-A disagreement (the reverse-anchor cosine percentile ranks Firm D fractionally above Firm C, while the mixture posterior and the box-rule rate rank Firm C highest among non-Firm-A firms).
|
||||
Three feature-derived scores agree on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$: the K=3 mixture posterior (a firm-compositional position score, not a mechanism cluster posterior); the reverse-anchor cosine percentile under a non-Big-4 reference distribution; and the deployed box-rule less-replication-dominated rate. The three scores are *not* statistically independent measurements — they are deterministic functions of the same per-CPA descriptor pair — so the convergence is documented as internal consistency rather than external validation against an independent ground truth (which the corpus does not provide for the hand-signed class). The strength of the convergence (all pairwise $|\rho| > 0.87$) and its persistence at the signature level (Cohen $\kappa = 0.87$ between per-CPA-fit and per-signature-fit K=3 binary labels) are nevertheless informative: per-CPA aggregation does not collapse the broad three-region ordering, and three different summarisations of the descriptor space produce broadly concordant per-CPA rankings, with a residual non-Firm-A disagreement (the reverse-anchor cosine percentile ranks Firm D fractionally above Firm C, while the mixture posterior and the deployed box-rule rate rank Firm C highest among non-Firm-A firms).
|
||||
|
||||
## F. Anchor-Based Multi-Level Calibration
|
||||
|
||||
The operational specificity of the deployed five-way classifier is characterised at three units of analysis (§III-L), all against the same inter-CPA negative-anchor coincidence-rate proxy. The per-comparison ICCR replicates v3.x's per-comparison rate (cos$>0.95 \to 0.00060$) and extends it to the structural dimension (dHash$\leq 5 \to 0.00129$; joint $\to 0.00014$). The pool-normalised per-signature ICCR captures the deployed rule's effective per-signature rate under inter-CPA candidate-pool replacement ($0.1102$ pooled Big-4 any-pair HC), exposing that the per-comparison rate is not the deployed-rule rate at the per-signature classifier level: the deployed classifier takes max-cosine and min-dHash over a same-CPA pool of size $n_{\text{pool}}$, so the inter-CPA-equivalent rate scales approximately as $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ in the independence limit. The per-document ICCR aggregates to operational alarm-rate units: HC alone $0.18$, the operational HC$+$MC alarm $0.34$.
|
||||
The operational specificity-proxy behaviour of the deployed HC sub-rule and document-level HC$+$MC alarm derived from the five-way classifier is characterised at three units of analysis (§III-L), all against the same inter-CPA negative-anchor coincidence-rate proxy. The per-comparison ICCR is consistent with the corpus-wide rate reported in §IV-I (cos$>0.95 \to 0.00060$) and extends it to the structural dimension (dHash$\leq 5 \to 0.00129$; joint $\to 0.00014$). The pool-normalised per-signature ICCR captures the deployed rule's effective per-signature rate under inter-CPA candidate-pool replacement ($0.1102$ pooled Big-4 any-pair HC), exposing that the per-comparison rate is not the deployed-rule rate at the per-signature classifier level: the deployed classifier takes max-cosine and min-dHash over a same-CPA pool of size $n_{\text{pool}}$, so the inter-CPA-equivalent rate scales approximately as $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ in the independence limit. The per-document ICCR aggregates to operational alarm-rate units: HC alone $0.18$, the operational HC$+$MC alarm $0.34$.
|
||||
|
||||
Two additional findings refine the calibration story. First, the per-pair conditional ICCR for dHash$\leq 5$ given cos$>0.95$ is $0.234$ (Wilson 95% $[0.190, 0.285]$): given the cosine gate, the structural dimension provides further per-comparison specificity at $\sim 4.3\times$ refinement. Second, the alert-rate sensitivity analysis (§III-L.5; Script 46) shows the inherited HC threshold is locally sensitive rather than plateau-stable (local gradient $\approx 25\times$ the median for cosine, $\approx 3.8\times$ for dHash); stakeholders requiring different specificity-alert-yield operating points can derive thresholds by inverting the ICCR curves (a tighter rule cos$>0.95$ AND dHash$\leq 3$ on the same-pair joint gives per-signature ICCR $\approx 0.045$). The MC/HSC sub-band boundary at dHash$=15$, by contrast, *is* plateau-like (local-to-median ratio $\approx 0.08$), consistent with high-dHash-tail saturation.
|
||||
Two additional findings refine the calibration story. First, the per-pair conditional ICCR for dHash$\leq 5$ given cos$>0.95$ is $0.234$ (Wilson 95% $[0.190, 0.285]$): given the cosine gate, the structural dimension provides further per-comparison specificity at $\sim 4.3\times$ refinement. Second, the alert-rate sensitivity analysis (§III-L.5) shows the deployed HC threshold is locally sensitive rather than plateau-stable (local gradient $\approx 25\times$ the median for cosine, $\approx 3.8\times$ for dHash); alternative operating points can be characterised by inverting the ICCR curves (e.g., a tighter rule cos$>0.95$ AND dHash$\leq 3$ on the same-pair joint corresponds to per-signature ICCR $\approx 0.045$). The MC/HSC sub-band boundary at dHash$=15$, by contrast, *is* plateau-like (local-to-median ratio $\approx 0.08$), consistent with high-dHash-tail saturation.
|
||||
|
||||
## G. Pixel-Identity as a Hard Positive Anchor; Inherited Inter-CPA Negative Anchor Reframed as Coincidence Rate
|
||||
## G. Pixel-Identity Positive Anchor and Inter-CPA Coincidence-Rate Negative Anchor
|
||||
|
||||
The only hard ground-truth subset in the corpus is pixel-identical signatures: those whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce byte-identical images, so these signatures are conservative-subset ground truth for the *replicated* class. On the Big-4 subset ($n = 262$ pixel-identical signatures), all three candidate checks — the inherited box rule, the K=3 hard label, and the reverse-anchor metric with a prevalence-calibrated cut — achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$). We caution that this result is necessary but not sufficient: for the box rule it is close to tautological, because byte-identical neighbours have cosine $\approx 1$ and dHash $\approx 0$, well inside the rule's high-confidence region. The corresponding signature-level *negative* anchor evidence is developed in §III-L.1 above (v4 spike: cos$>0.95$ per-comparison ICCR $= 0.00060$, replicating v3.20.0's reported $0.0005$ under prior "FAR" terminology). We frame the per-comparison rate as a specificity proxy under the assumption that inter-CPA pairs constitute a clean negative anchor, and we document in §III-L.4 that this assumption is partially violated by within-firm cross-CPA template-like collision structures.
|
||||
The only conservative hard-positive subset in the corpus is pixel-identical signatures: those whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce byte-identical images, so these signatures are a conservative hard-positive subset for image replication. On the Big-4 subset ($n = 262$ pixel-identical signatures), all three candidate checks — the deployed box rule, the K=3 hard label, and the reverse-anchor metric with a prevalence-calibrated cut — achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$). We caution that this result is necessary but not sufficient: for the deployed box rule it is close to tautological, because byte-identical neighbours have cosine $\approx 1$ and dHash $\approx 0$, well inside the rule's high-confidence region. The corresponding signature-level *negative* anchor evidence is developed in §III-L.1 above (per-comparison ICCR $= 0.00060$ at cos$>0.95$, consistent with the corpus-wide rate of $0.0005$ reported in §IV-I). We frame the per-comparison rate as a specificity proxy under the assumption that inter-CPA pairs constitute a clean negative anchor, and we document in §III-L.4 that this assumption is partially violated by within-firm cross-CPA template-like collision structures.
|
||||
|
||||
## H. Limitations
|
||||
|
||||
Several limitations should be transparent. The first nine are v4.0-specific; the last five are inherited from v3.20.0 §V-G and still apply to the v4.0 pipeline.
|
||||
Several limitations should be transparent. We group them into primary methodological limitations, secondary scope and validation caveats, documented design features, and engineering-level caveats of the pipeline.
|
||||
|
||||
**Primary methodological limitations.**
|
||||
|
||||
*No signature-level ground truth; no true error rates reportable.* The corpus does not contain labelled hand-signed or replicated classes at the signature level. We therefore cannot report False Rejection Rate, sensitivity, recall, Equal Error Rate, ROC-AUC, precision, or positive predictive value against ground truth. All quantitative rates reported in §III-L are inter-CPA negative-anchor coincidence rates (ICCRs) under the assumption that inter-CPA pairs constitute a clean negative anchor; this is a specificity proxy, not a calibrated specificity (§III-M).
|
||||
|
||||
*Inter-CPA negative-anchor assumption is partially violated and the violation is firm-dependent.* The cross-firm hit matrix of §III-L.4 shows that under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms), consistent with firm-specific template, stamp, or document-production reuse. The inter-CPA-as-negative assumption is therefore not exactly satisfied — some inter-CPA pairs may share firm-level templates rather than being independent random matches. Our reported per-comparison ICCRs are best read as specificity-proxy rates under a partially-violated assumption, not as calibrated FARs. Because the violation is firm-dependent, Firm A's per-firm ICCR is more contaminated by within-firm sharing than Firms B/C/D's; the per-firm B/C/D rates of $0.09$–$0.16$ are therefore closer to a clean specificity estimate than the pooled rate, and the Firm A vs Firms B/C/D contrast reflects both genuine firm heterogeneity and a firm-dependent proxy-contamination gradient.
|
||||
*Inter-CPA negative-anchor assumption is partially violated and the violation is firm-dependent.* The cross-firm hit matrix of §III-L.4 shows that under the deployed any-pair rule, within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms), consistent with firm-specific template, stamp, or document-production reuse. The inter-CPA-as-negative assumption is therefore not exactly satisfied — some inter-CPA pairs may share firm-level templates rather than being independent random matches. Our reported per-comparison ICCRs are best read as specificity-proxy rates under a partially-violated assumption, not as calibrated FARs. Because the violation is firm-dependent, Firm A's per-firm ICCR is more contaminated by within-firm sharing than Firms B/C/D's; the per-firm B/C/D rates of $0.09$–$0.16$ may therefore be less contaminated than the pooled rate, and the Firm A vs Firms B/C/D contrast reflects both genuine firm heterogeneity and a firm-dependent proxy-contamination gradient.
|
||||
|
||||
*Scope.* The v4.0 primary analyses are scoped to the Big-4 sub-corpus. We did not perform the full per-signature pool-normalised ICCR analysis at the full $n = 686$ scope; the §IV-K full-dataset Spearman re-run shows the K=3 $+$ box-rule rank-convergence is preserved at $n = 686$ but does not validate the Big-4 operational ICCRs, the LOOO firm-fold structure, or the five-way operational classifier at the broader scope.
|
||||
*Mechanism attribution for the firm-level heterogeneity is not identifiable from descriptor-only data.* The observed firm-level contrast (Firm A's per-document HC$+$MC ICCR of $0.62$ versus $0.09$–$0.16$ at Firms B/C/D; within-firm collision concentration $77$–$99\%$ under the deployed any-pair rule; byte-identical evidence of §IV-H) is consistent with at least three non-mutually-exclusive firm-level mechanisms: (i) template, stamp, or e-signature production reuse; (ii) digitisation-pipeline homogeneity — shared scanners, common PDF generation infrastructure, identical compression and form-template settings — that systematically inflates image-descriptor similarity without signature replication; and (iii) signing-style or training homogeneity that produces correlated handwritten signatures within a firm. The descriptor pair (cosine, dHash) operates at the image-similarity level and is, by construction, indifferent to which mechanism generated a given near-identical pair. We therefore report the firm contrast as a methodological observation — the framework discriminates at firm-level resolution — rather than as a mechanism finding. The byte-identical Firm A signatures across $\sim 50$ distinct partners (§IV-H, §V-C) provide direct evidence for (i) at Firm A specifically, but do not exclude additive contribution from (ii) or (iii); the milder within-firm collision patterns at Firms B/C/D are individually consistent with all three mechanisms. Image-acquisition metadata (scanner identifiers, PDF generator fingerprints, compression-codec markers), partner-level intent records, or controlled hand-signed baselines would be needed to attribute the contrast across (i), (ii), and (iii).
|
||||
|
||||
*Pixel-identity is a conservative subset.* Byte-identical pairs are the easiest replicated cases, and for the inherited box rule the positive-anchor miss rate against byte-identical pairs is close to tautological (byte-identical $\Rightarrow$ cosine $\approx 1$, dHash $\approx 0$, well inside the high-confidence box). A score that fails the pixel-identity check would be disqualified, but passing the check does not guarantee correct behaviour on the broader replicated population (e.g., re-stamped or noisy-template-variant signatures).
|
||||
*Scope.* The primary analyses are scoped to the Big-4 sub-corpus. We did not perform the full per-signature pool-normalised ICCR analysis at the full $n = 686$ scope; the §IV-K full-dataset Spearman re-run shows the K=3 $+$ deployed box-rule rank-convergence is preserved at $n = 686$ but does not establish portability of the Big-4 operational ICCRs, the LOOO firm-fold structure, or the five-way operational classifier at the broader scope.
|
||||
|
||||
*Inherited rule components are not separately v4-validated.* The five-way classifier's moderate-confidence band (cos $> 0.95$ AND $5 < \text{dHash} \leq 15$), the style-consistency band ($\text{dHash} > 15$), and the document-level worst-case aggregation rule retain their v3.20.0 calibration and capture-rate evidence; v4.0's anchor-based ICCR calibration covers the binary high-confidence sub-rule (and its tightening alternatives such as dHash$\leq 3$), and the alert-rate sensitivity analysis (§III-L.5) characterises only the HC threshold. The MC and HSC sub-band boundaries are not separately re-validated by v4.0's diagnostic battery.
|
||||
**Secondary scope and validation caveats.**
|
||||
|
||||
*Pixel-identity is a conservative subset.* Byte-identical pairs are the easiest replicated cases, and for the deployed box rule the positive-anchor miss rate against byte-identical pairs is close to tautological (byte-identical $\Rightarrow$ cosine $\approx 1$, dHash $\approx 0$, well inside the high-confidence box). A score that fails the pixel-identity check would be disqualified, but passing the check does not guarantee correct behaviour on the broader replicated population (e.g., re-stamped or noisy-template-variant signatures).
|
||||
|
||||
*Rule components not separately re-characterised by the present diagnostic battery.* The five-way classifier's moderate-confidence band (cos $> 0.95$ AND $5 < \text{dHash} \leq 15$), the style-consistency band ($\text{dHash} > 15$), and the document-level worst-case aggregation rule retain their prior calibration and capture-rate evidence (supplementary materials); the anchor-based ICCR calibration covers the binary high-confidence sub-rule (and its tightening alternatives such as dHash$\leq 3$), and the alert-rate sensitivity analysis (§III-L.5) characterises only the HC threshold. The MC and HSC sub-band boundaries are not separately re-characterised by the present diagnostic battery.
|
||||
|
||||
*Deployed-rate excess is not a presumed true-positive rate.* The $\sim 44$-pp per-document gap between the observed deployed alert rate (HC: $0.62$ on real same-CPA pools) and the inter-CPA proxy rate (HC: $0.18$) cannot be interpreted as a presumed true-positive rate without additional assumptions that §III-M shows are unsafe (consistent within-CPA signing can exceed inter-CPA similarity at the cosine axis; within-firm template sharing inflates the inter-CPA proxy baseline). The gap is best read as a same-CPA repeatability signal.
|
||||
|
||||
*A1 pair-detectability stipulation.* The per-signature detector requires at least one same-CPA pair to be near-identical when a CPA uses image replication. A1 is plausible for high-volume stamping or firm-level electronic signing but not guaranteed when a corpus contains only one observed replicated report for a CPA, multiple template variants used in parallel, or scan-stage noise that pushes a replicated pair outside the detection regime.
|
||||
|
||||
*K=3 hard-posterior membership is composition-sensitive.* The K=3 hard-posterior membership for any single firm varies by up to $12.8$ pp across LOOO folds. This is documented as a composition-sensitivity band rather than failure, but it means K=3 hard labels are not used as v4.0 operational classifier output; they are reported only as accountant-level descriptive characterisation.
|
||||
**Documented design features.**
|
||||
|
||||
*No partner-level mechanism attribution.* v4.0 reports population-level patterns; it does not perform partner-level mechanism attribution or report-level claims of intent. The signature-level outputs are signature-level quantities throughout. The within-firm cross-CPA collision concentration of §III-L.4 is consistent with template-like reuse but is not by itself diagnostic of deliberate sharing.
|
||||
*K=3 hard-posterior membership is composition-sensitive.* The K=3 hard-posterior membership for any single firm varies by up to $12.8$ pp across LOOO folds. This is documented as a composition-sensitivity band rather than failure, but it means K=3 hard labels are not used as operational classifier output; they are reported only as accountant-level descriptive characterisation.
|
||||
|
||||
*Transferred ImageNet features (inherited from v3.20.0).* The ResNet-50 feature extractor uses pre-trained ImageNet weights without signature-domain fine-tuning. While our backbone-ablation study (§IV-L, inherited from v3.20.0 §IV-I) and prior literature support the effectiveness of transferred ImageNet features for signature comparison, a signature-domain fine-tuned feature extractor could improve discriminative performance.
|
||||
*No partner-level mechanism attribution.* The analysis reports population-level patterns; it does not perform partner-level mechanism attribution or report-level claims of intent. The signature-level outputs are signature-level quantities throughout. The within-firm cross-CPA collision concentration of §III-L.4 is consistent with template-like reuse but is not by itself diagnostic of deliberate sharing.
|
||||
|
||||
*Red-stamp HSV preprocessing artifacts (inherited from v3.20.0).* The red stamp removal preprocessing uses simple HSV color-space filtering, which may introduce artifacts where handwritten strokes overlap with red seal impressions. Blended pixels are replaced with white, potentially creating small gaps in signature strokes that could reduce dHash similarity. This bias would push classifications toward false negatives rather than false positives.
|
||||
**Engineering-level caveats of the pipeline.**
|
||||
|
||||
*Longitudinal scan / PDF / compression confounds (inherited from v3.20.0).* Scanning equipment, PDF generation software, and compression algorithms may have changed over the 2013–2023 study period, potentially affecting similarity measurements. While cosine similarity and dHash are designed to be robust to such variations, longitudinal confounds cannot be entirely excluded.
|
||||
*Transferred ImageNet features.* The ResNet-50 feature extractor uses pre-trained ImageNet weights without signature-domain fine-tuning. While our backbone-ablation study (§IV-L) and prior literature support the effectiveness of transferred ImageNet features for signature comparison, a signature-domain fine-tuned feature extractor could improve discriminative performance.
|
||||
|
||||
*Source-exemplar misattribution in max/min pair logic (inherited from v3.20.0).* The max-cosine / min-dHash detection logic treats both ends of a near-identical same-CPA pair as non-hand-signed. In the rare case where one of the two documents contains a genuinely hand-signed exemplar that was subsequently reused as a stamping or e-signature template, the pair correctly identifies image reuse but misattributes non-hand-signed status to the source exemplar. This affects at most one source document per template variant per CPA and is not expected to be common.
|
||||
*Red-stamp HSV preprocessing artifacts.* The red stamp removal preprocessing uses simple HSV color-space filtering, which may introduce artifacts where handwritten strokes overlap with red seal impressions. Blended pixels are replaced with white, potentially creating small gaps in signature strokes that could reduce dHash similarity. This bias would push classifications toward false negatives rather than false positives.
|
||||
|
||||
*Legal and regulatory interpretation (inherited from v3.20.0).* Whether non-hand-signing of a CPA's own stored signature constitutes a violation of signing requirements is a jurisdiction-specific legal question. Our technical analysis can inform such determinations but cannot resolve them.
|
||||
*Longitudinal scan / PDF / compression confounds.* Scanning equipment, PDF generation software, and compression algorithms may have changed over the 2013–2023 study period, potentially affecting similarity measurements. While cosine similarity and dHash are designed to be robust to such variations, longitudinal confounds cannot be entirely excluded.
|
||||
|
||||
*Source-exemplar misattribution in max/min pair logic.* The max-cosine / min-dHash detection logic treats both ends of a near-identical same-CPA pair as non-hand-signed. In the rare case where one of the two documents contains a genuinely hand-signed exemplar that was subsequently reused as a stamping or e-signature template, the pair correctly identifies image reuse but misattributes non-hand-signed status to the source exemplar. This affects at most one source document per template variant per CPA and is not expected to be common.
|
||||
|
||||
*Legal and regulatory interpretation.* Whether non-hand-signing of a CPA's own stored signature constitutes a violation of signing requirements is a jurisdiction-specific legal question. Our technical analysis can inform such determinations but cannot resolve them.
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<!--
|
||||
ARCHIVED. Not part of the IEEE Access submission.
|
||||
|
||||
IEEE Access Regular Papers do not include a separate Impact Statement
|
||||
section. The text below is retained for possible reuse in a cover
|
||||
letter, grant report, or non-IEEE venue. It is excluded from the
|
||||
assembled paper by export_v3.py.
|
||||
|
||||
If reused, note that the wording "distinguishes genuinely hand-signed
|
||||
ARCHIVED. Not part of the IEEE Access submission. The block below is wrapped
|
||||
in an HTML comment so it does not render in the assembled paper. It is
|
||||
retained for possible reuse in a cover letter, grant report, or non-IEEE
|
||||
venue. If reused, note that the wording "distinguishes genuinely hand-signed
|
||||
signatures from reproduced ones" overstates what a five-way confidence
|
||||
classifier without a fully labeled test set establishes; soften before
|
||||
external use.
|
||||
-->
|
||||
|
||||
# Impact Statement (archived; not in IEEE Access submission)
|
||||
|
||||
Auditor signatures on financial reports are a key safeguard of corporate accountability.
|
||||
When the signature on an audit report is produced by reproducing a stored image instead of by the partner's own hand---whether through an administrative stamping workflow or a firm-level electronic signing system---this safeguard is weakened, yet detecting the practice through manual inspection is infeasible at the scale of modern financial markets.
|
||||
We developed a pipeline that automatically extracts and analyzes signatures from over 90,000 audit reports spanning a decade of filings by publicly listed companies in Taiwan.
|
||||
Combining deep-learning visual features with perceptual hashing and two methodologically distinct threshold estimators (plus a density-smoothness diagnostic), the system stratifies signatures into a five-way confidence-graded classification and quantifies how the practice varies across firms and over time.
|
||||
After further validation, the technology could support financial regulators in screening signature authenticity at national scale.
|
||||
Combining deep-learning visual features with perceptual hashing, distributional diagnostics, and anchor-based inter-CPA coincidence-rate calibration, the system stratifies signatures into a five-way confidence-graded classification and quantifies how the practice varies across firms and over time.
|
||||
With a future labelled evaluation set, the technology could support financial regulators in screening candidate non-hand-signed signatures at national scale.
|
||||
-->
|
||||
|
||||
@@ -10,17 +10,17 @@ The distinction between *non-hand-signing detection* and *signature forgery dete
|
||||
|
||||
A methodological concern shapes the research design. Many prior similarity-based classification studies rely on ad-hoc thresholds — declaring two images equivalent above a hand-picked cosine cutoff, for example — without principled statistical justification. Such thresholds are fragile in an archival-data setting. A defensible approach requires (i) explicit calibration of the operational thresholds against measurable negative-anchor evidence; (ii) diagnostic procedures that test whether the descriptor distribution itself supports a within-population threshold, including formal decomposition of apparent multimodality into between-group composition and integer-tie artefacts; (iii) annotation-free reporting of operational alarm rates at multiple analysis units (per-comparison, per-signature pool, per-document) with Wilson 95% confidence intervals; (iv) per-firm stratification of the reported rates to surface heterogeneity that aggregate metrics conceal; and (v) explicit disclosure of the unsupervised setting's limits — in particular, the inability to estimate true error rates without signature-level ground-truth labels.
|
||||
|
||||
Despite the significance of the problem for audit quality and regulatory oversight, no prior work has specifically addressed non-hand-signing detection in financial audit documents at scale with these methodological safeguards. Woodruff et al. [9] developed an automated pipeline for signature analysis in corporate filings for anti-money-laundering investigations, but their work focused on author clustering rather than detecting image reuse. Copy-move forgery detection methods [10], [11] address duplicated regions within or across images but are designed for natural images and do not account for the specific characteristics of scanned document signatures. Research on near-duplicate image detection using perceptual hashing combined with deep learning [12], [13] provides relevant methodological foundations but has not been applied to document forensics or signature analysis. From the statistical side, the methods we adopt for distributional characterisation — the Hartigan dip test [37] and finite mixture modelling via the EM algorithm [40], [41], complemented by a Burgstahler-Dichev / McCrary density-smoothness diagnostic [38], [39] — have been developed in statistics and accounting-econometrics but have not been combined as a joint diagnostic toolkit for document-forensics threshold characterisation.
|
||||
Despite the significance of the problem for audit quality and regulatory oversight, to our knowledge no prior work has specifically addressed non-hand-signing detection in financial audit documents at scale with these methodological safeguards. Woodruff et al. [9] developed an automated pipeline for signature analysis in corporate filings for anti-money-laundering investigations, but their work focused on author clustering rather than detecting image reuse. Copy-move forgery detection methods [10], [11] address duplicated regions within or across images but are designed for natural images and do not account for the specific characteristics of scanned document signatures. Research on near-duplicate image detection using perceptual hashing combined with deep learning [12], [13] provides relevant methodological foundations but has not been applied to document forensics or signature analysis. From the statistical side, the methods we adopt for distributional characterisation — the Hartigan dip test [37] and finite mixture modelling via the EM algorithm [40], [41], complemented by a Burgstahler-Dichev / McCrary density-smoothness diagnostic [38], [39] — have been developed in statistics and accounting-econometrics but have not been combined as a joint diagnostic toolkit for document-forensics threshold characterisation.
|
||||
|
||||
In this paper we present a fully automated, end-to-end pipeline for detecting non-hand-signed CPA signatures in audit reports at scale, together with a multi-tool validation framework that explicitly discloses the unsupervised setting's limits. The pipeline processes raw PDF documents through (1) signature page identification with a Vision-Language Model; (2) signature region detection with a trained YOLOv11 object detector; (3) deep feature extraction via a pre-trained ResNet-50; (4) dual-descriptor similarity (cosine + independent-minimum dHash); (5) anchor-based threshold calibration at three units of analysis (per-comparison, pool-normalised per-signature, per-document) against an inter-CPA negative-anchor coincidence-rate proxy (§III-L); (6) firm-stratified per-rule reporting and a within-firm cross-CPA hit-matrix analysis (§III-L.4); (7) a composition decomposition that establishes the absence of a within-population bimodal antimode in the descriptor distributions (§III-I.4); and (8) a multi-tool unsupervised validation strategy with disclosed assumption-violation analysis (§III-M).
|
||||
In this paper we present a fully automated, end-to-end pipeline for screening non-hand-signed CPA signatures in audit reports at scale, together with an anchor-calibrated screening framework that characterises the pipeline's operational behaviour under explicit unsupervised assumptions. The pipeline processes raw PDF documents through (1) signature page identification with a Vision-Language Model; (2) signature region detection with a trained YOLOv11 object detector; (3) deep feature extraction via a pre-trained ResNet-50; (4) dual-descriptor similarity (cosine + independent-minimum dHash); (5) anchor-based threshold calibration at three units of analysis (per-comparison, pool-normalised per-signature, per-document) against an inter-CPA negative-anchor coincidence-rate proxy (§III-L); (6) firm-stratified per-rule reporting and a within-firm cross-CPA hit-matrix analysis (§III-L.4); (7) a composition decomposition that establishes the absence of a within-population bimodal antimode in the descriptor distributions (§III-I.4); and (8) disclosure of each diagnostic's untested assumption (§III-M).
|
||||
|
||||
The methodological reframing relative to earlier versions of this work is central to our v4.0 contribution. Earlier work in this lineage adopted a distributional path to thresholds — fitting accountant-level finite-mixture models and treating their marginal crossings as data-derived "natural" thresholds. v4.0 reports a composition decomposition diagnostic (§III-I.4) that overturns this reading: the apparent multimodality of the Big-4 accountant-level distribution is fully explained by between-firm location-shift effects (Firm A's mean dHash of $2.73$ versus Firms B/C/D's $6.46$, $7.39$, $7.21$) and integer mass-point artefacts on the integer-valued dHash axis. Once both confounds are removed (firm-mean centring plus uniform integer jitter), the Big-4 pooled dHash dip test yields $p_{\text{median}} = 0.35$ across five jitter seeds, eliminating the rejection. Within-firm signature-level cosine dip tests fail to reject in every individual Big-4 firm and in every individual mid/small firm with $\geq 500$ signatures (10 firms tested in Script 39c), and the corresponding within-firm jittered-dHash dip tests likewise fail to reject in all four Big-4 firms (Script 39d) and across a codex-verified read-only spike on the same ten mid/small firms ($0/10$ reject; §III-I.4). The descriptor distributions therefore contain no within-population bimodal antimode that could anchor an operational threshold.
|
||||
A key empirical finding is that the descriptor distributions do not support a within-population natural threshold. The apparent multimodality in the Big-4 accountant-level distribution is explained by between-firm location-shift effects (Firm A's mean dHash of $2.73$ versus Firms B/C/D's $6.46$, $7.39$, $7.21$) and integer mass-point artefacts on the integer-valued dHash axis. After joint firm-mean centring and uniform integer-tie jitter, the pooled dHash dip-test rejection disappears ($p_{\text{median}} = 0.35$ across five seeds). Within-firm diagnostics in every Big-4 firm fail to reveal stable bimodal structure after accounting for integer ties; eligible non-Big-4 firms provide corroborating raw-axis evidence on the cosine dimension (§III-I.4). We therefore treat mixture fits as descriptive summaries of firm-compositional structure rather than threshold-generating mechanisms, and calibrate the deployed operating rules using inter-CPA coincidence-rate anchors.
|
||||
|
||||
In place of distributional anchoring, v4.0 adopts an anchor-based inter-CPA coincidence-rate (ICCR) calibration. At the per-comparison unit, the inherited cos$>0.95$ operating point yields ICCR $= 0.00060$ on a $5 \times 10^5$-pair Big-4 sample (replicating v3.x's reported per-comparison rate of $0.0005$ under prior "FAR" terminology); the dHash$\leq 5$ structural cutoff yields ICCR $= 0.00129$ (v4 new); the joint rule cos$>0.95$ AND dHash$\leq 5$ yields joint ICCR $= 0.00014$ (any-pair semantics, matching the deployed extrema rule). At the pool-normalised per-signature unit, the same rule's effective coincidence rate is materially higher because the deployed classifier takes max-cosine and min-dHash over a same-CPA pool: pooled Big-4 any-pair ICCR is $0.1102$ (Wilson 95% CI $[0.1086, 0.1118]$; CPA-block bootstrap 95% $[0.0908, 0.1330]$). At the per-document unit, the operational HC$+$MC alarm fires on $33.75\%$ of Big-4 documents under the inter-CPA candidate-pool counterfactual.
|
||||
In place of distributional anchoring, we adopt an anchor-based inter-CPA coincidence-rate (ICCR) calibration. At the per-comparison unit, the cos$>0.95$ operating point yields ICCR $= 0.00060$ on a $5 \times 10^5$-pair Big-4 sample; the dHash$\leq 5$ structural cutoff yields ICCR $= 0.00129$; the joint rule cos$>0.95$ AND dHash$\leq 5$ yields joint ICCR $= 0.00014$ (any-pair semantics, matching the deployed extrema rule). At the pool-normalised per-signature unit, the same rule's effective coincidence rate is materially higher because the deployed classifier takes max-cosine and min-dHash over a same-CPA pool: pooled Big-4 any-pair ICCR is $0.1102$ (Wilson 95% CI $[0.1086, 0.1118]$; CPA-block bootstrap 95% $[0.0908, 0.1330]$). At the per-document unit, the operational HC$+$MC alarm fires on $33.75\%$ of Big-4 documents under the inter-CPA candidate-pool counterfactual.
|
||||
|
||||
The pooled per-signature and per-document rates conceal striking firm heterogeneity. A logistic regression of the per-signature hit indicator on firm dummies (Firm A reference) and centred log pool size yields odds ratios of $0.053$ (Firm B), $0.010$ (Firm C), and $0.027$ (Firm D) — Firms B/C/D are an order of magnitude below Firm A even after controlling for the pool-size confound (Script 44). Cross-firm hit matrix analysis under the deployed any-pair rule shows within-firm collision concentrations of $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (Table XXV; the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms). The pattern is consistent with firm-specific template, stamp, or document-production reuse mechanisms — though not by itself diagnostic of deliberate sharing. We retain the inherited Paper A v3.x five-way box rule as the operational classifier; v4.0's contribution is to characterise its multi-level coincidence behaviour against the inter-CPA negative anchor rather than to derive new thresholds.
|
||||
The pooled per-signature and per-document rates conceal striking firm heterogeneity. A logistic regression of the per-signature hit indicator on firm dummies (Firm A reference) and centred log pool size yields odds ratios of $0.053$ (Firm B), $0.010$ (Firm C), and $0.027$ (Firm D) — Firms B/C/D are an order of magnitude below Firm A even after controlling for the pool-size confound. Cross-firm hit matrix analysis under the deployed any-pair rule shows within-firm collision concentrations of $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D (Table XXV; the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms). The pattern is consistent with firm-specific template, stamp, or document-production reuse mechanisms — though not by itself diagnostic of deliberate sharing. The deployed five-way box rule defines a reproducible screening classifier; the calibration contribution is to characterise its multi-level inter-CPA coincidence behaviour rather than to derive new thresholds. The high-confidence sub-rule (cos $> 0.95$ AND dHash $\leq 5$) and moderate-confidence sub-rule (cos $> 0.95$ AND $5 < \text{dHash} \leq 15$) are explicit decision rules whose calibrated false-positive and false-negative error rates remain unknown in the absence of signature-level labels.
|
||||
|
||||
Three feature-derived scores converge on the per-CPA descriptor-position ranking with Spearman $\rho \geq 0.879$ (Script 38): the K=3 mixture posterior (now interpreted as a firm-compositional position score, not a mechanism cluster posterior; §III-J), a reverse-anchor cosine percentile relative to a strictly-out-of-target non-Big-4 reference, and the inherited box-rule less-replication-dominated rate. The three scores are deterministic functions of the same per-CPA descriptor pair, so the convergence is documented as internal consistency among feature-derived ranks rather than external validation. Hard ground truth for the *replicated* class is provided by 262 byte-identical signatures in the Big-4 subset (Firm A 145, Firm B 8, Firm C 107, Firm D 2), against which all three candidate checks achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$). For the box rule this result is close to tautological at byte-identity; we discuss the conservative-subset caveat in §V-G.
|
||||
Three feature-derived scores converge on the per-CPA descriptor-position ranking with Spearman $\rho \geq 0.879$: the K=3 mixture posterior (a firm-compositional position score under §III-J's reading, not a mechanism cluster posterior), a reverse-anchor cosine percentile relative to a strictly-out-of-target non-Big-4 reference, and the box-rule less-replication-dominated rate. The three scores are deterministic functions of the same per-CPA descriptor pair, so the convergence is documented as internal consistency among feature-derived ranks rather than external validation. A conservative hard-positive subset for image replication is provided by 262 byte-identical signatures in the Big-4 subset (Firm A 145, Firm B 8, Firm C 107, Firm D 2), against which all three candidate checks achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$). For the box rule this result is close to tautological at byte-identity; we discuss the conservative-subset caveat in §V-G.
|
||||
|
||||
We apply this pipeline to 90,282 audit reports filed by publicly listed companies in Taiwan between 2013 and 2023, extracting and analyzing 182,328 individual CPA signatures from 758 unique accountants. The Big-4 sub-corpus comprises 437 CPAs and 150,442 signatures with both descriptors available.
|
||||
|
||||
@@ -30,16 +30,16 @@ The contributions of this paper are:
|
||||
|
||||
2. **End-to-end pipeline.** We present a pipeline that processes raw PDF audit reports through VLM-based page identification, YOLO-based signature detection, ResNet-50 feature extraction, and dual-descriptor similarity computation, with automated inference and no manual intervention after initial training.
|
||||
|
||||
3. **Dual-descriptor verification.** We demonstrate that combining deep-feature cosine similarity with independent-minimum dHash resolves the ambiguity between *style consistency* and *image reproduction*, and we validate the backbone choice through a feature-backbone ablation.
|
||||
3. **Dual-descriptor similarity.** We demonstrate that combining deep-feature cosine similarity with independent-minimum dHash provides complementary evidence for screening cases where *style consistency* and *image reproduction* hypotheses diverge, and we support the backbone choice through a feature-backbone ablation.
|
||||
|
||||
4. **Composition decomposition disproves the distributional-threshold path.** We show via a 2×2 factorial diagnostic (firm-mean centring × integer-tie jitter) that the apparent multimodality of the Big-4 accountant-level descriptor distribution is fully attributable to between-firm location shifts and integer mass-point artefacts. The descriptor distributions contain no within-population bimodal antimode; "natural threshold" language in this lineage's prior work is not empirically supported.
|
||||
4. **Composition decomposition does not support the distributional-threshold path.** We show via a 2×2 factorial diagnostic (firm-mean centring × integer-tie jitter) that the apparent multimodality of the Big-4 accountant-level descriptor distribution is fully attributable to between-firm location shifts and integer mass-point artefacts. The descriptor distributions contain no within-population bimodal antimode; a distributional "natural threshold" reading of the operating points is not empirically supported.
|
||||
|
||||
5. **Anchor-based multi-level inter-CPA coincidence-rate calibration.** We characterise the deployed five-way classifier at three units of analysis: per-comparison ICCR (cos$>0.95$: $0.0006$; dHash$\leq 5$: $0.0013$; joint: $0.00014$), pool-normalised per-signature ICCR ($0.11$ for the deployed any-pair high-confidence rule), and per-document ICCR ($0.34$ for the operational HC$+$MC alarm). We adopt "inter-CPA coincidence rate" as the metric name throughout and reserve "False Acceptance Rate" for terminology that requires ground-truth negative labels, which the corpus does not provide.
|
||||
5. **Anchor-based multi-level inter-CPA coincidence-rate calibration.** We characterise the deployed high-confidence (HC) sub-rule and document-level HC$+$MC alarm derived from the five-way classifier at three units of analysis: per-comparison ICCR (cos$>0.95$: $0.0006$; dHash$\leq 5$: $0.0013$; joint: $0.00014$), pool-normalised per-signature ICCR ($0.11$ for the deployed any-pair high-confidence rule), and per-document ICCR ($0.34$ for the operational HC$+$MC alarm). We adopt "inter-CPA coincidence rate" as the metric name throughout and reserve "False Acceptance Rate" for terminology that requires ground-truth negative labels, which the corpus does not provide.
|
||||
|
||||
6. **Firm heterogeneity quantification and within-firm cross-CPA collision concentration.** Per-firm rates differ by an order of magnitude after pool-size adjustment (Firm A's per-document HC$+$MC alarm at $0.62$ versus Firms B/C/D at $0.09$–$0.16$). Cross-firm hit matrix analysis shows within-firm collision concentrations of $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D under the deployed any-pair rule (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms); the pattern is consistent with firm-specific template, stamp, or document-production reuse mechanisms — a descriptive finding about deployed-rule behaviour, not a claim of deliberate template sharing.
|
||||
6. **Firm heterogeneity quantification and within-firm cross-CPA collision concentration.** Per-document D2 inter-CPA proxy ICCRs differ by an order of magnitude across firms (Firm A: $0.62$ versus Firms B/C/D: $0.09$–$0.16$); a per-signature logistic regression of the any-pair HC hit indicator on firm dummies and centred log pool size confirms the firm gap persists after pool-size control. Cross-firm hit matrix analysis shows within-firm collision concentrations of $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D under the deployed any-pair rule (the stricter same-pair joint event saturates at $97.0$–$99.96\%$ within-firm across all four firms); the pattern is consistent with — but does not independently establish — firm-level template-like reuse, digitisation-pipeline homogeneity, or signing-style homogeneity, which descriptor-only data cannot separate (§V-H).
|
||||
|
||||
7. **K=3 as descriptive firm-compositional partition; three-score convergent internal consistency.** We fit a K=3 Gaussian mixture as a descriptive partition of the Big-4 accountant-level distribution (no longer interpreted as three mechanism clusters). Three feature-derived scores agree on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$; we report this as internal consistency rather than external validation, given that the scores share the underlying descriptor pair.
|
||||
7. **K=3 as descriptive firm-compositional partition; three-score convergent internal consistency.** We fit a K=3 Gaussian mixture as a descriptive partition of the Big-4 accountant-level distribution (interpreted as firm-compositional structure, not as three mechanism clusters). Three feature-derived scores agree on the per-CPA descriptor-position ranking at Spearman $\rho \geq 0.879$; we report this as internal consistency rather than external validation, given that the scores share the underlying descriptor pair.
|
||||
|
||||
8. **Annotation-free positive-anchor validation and unsupervised validation ceiling.** We achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$) on 262 byte-identical Big-4 signatures, with the conservative-subset caveat that byte-identical pairs are by construction near cos$=1$ and dHash$=0$. We frame the overall validation strategy as a multi-tool collection of ten partial-evidence diagnostics (§III-M Table XXVII), each with an explicitly disclosed untested assumption; their conjunction constitutes the unsupervised validation ceiling achievable on this corpus. We do not claim a validated forensic detector; we position the system as a specificity-proxy-anchored screening framework with human-in-the-loop review.
|
||||
8. **Annotation-free positive-anchor capture check and unsupervised-setting disclosure.** We achieve $0\%$ positive-anchor miss rate (Wilson 95% upper bound $1.45\%$) on 262 byte-identical Big-4 signatures, with the conservative-subset caveat that byte-identical pairs are by construction near cos$=1$ and dHash$=0$. Each supporting diagnostic in §III-M addresses one specific failure mode of an unsupervised screening classifier — composition artefacts, inter-CPA coincidence, pool-size confounding, firm heterogeneity, threshold sensitivity, or positive-anchor capture — with an explicitly disclosed untested assumption. We do not claim a validated forensic detector; we position the system as a specificity-proxy-anchored screening framework with human-in-the-loop review.
|
||||
|
||||
The remainder of the paper is organized as follows. Section II reviews related work on signature verification, document forensics, perceptual hashing, and the statistical methods used. Section III describes the proposed methodology. Section IV presents the experimental results — distributional characterisation, mixture fits, convergent internal-consistency checks, leave-one-firm-out reproducibility, pixel-identity validation, and full-dataset robustness. Section V discusses the implications and limitations. Section VI concludes with directions for future work.
|
||||
The remainder of the paper is organized as follows. Section II reviews related work on signature verification, document forensics, perceptual hashing, and the statistical methods used. Section III describes the proposed methodology. Section IV presents the experimental results — distributional characterisation, mixture fits, convergent internal-consistency checks, leave-one-firm-out reproducibility, pixel-identity positive-anchor check, and full-dataset robustness. Section V discusses the implications and limitations. Section VI concludes with directions for future work.
|
||||
|
||||
+97
-127
@@ -4,19 +4,19 @@
|
||||
|
||||
We propose a six-stage pipeline for large-scale non-hand-signed auditor signature detection in scanned financial documents.
|
||||
Fig. 1 illustrates the overall architecture.
|
||||
The pipeline takes as input a corpus of PDF audit reports and produces, for each document, a classification of its CPA signatures along a confidence continuum anchored on whole-sample Firm A percentile heuristics and validated against a byte-level pixel-identity positive anchor and a large random inter-CPA negative anchor.
|
||||
The pipeline takes as input a corpus of PDF audit reports and produces five-way operational screening labels (§III-H.1) whose behaviour is characterised by pixel-identity positive-anchor capture checks and inter-CPA coincidence-rate calibration (§III-L).
|
||||
|
||||
Throughout this paper we use the term *non-hand-signed* rather than "digitally replicated" to denote any signature produced by reproducing a previously stored image of the partner's signature---whether by administrative stamping workflows (dominant in the early years of the sample) or firm-level electronic signing systems (dominant in the later years).
|
||||
From the perspective of the output image the two workflows are equivalent: both can reproduce one or more stored signature images, producing same-CPA signatures that are identical or near-identical up to reproduction, scanning, compression, and template-variant noise.
|
||||
|
||||
<!--
|
||||
[Figure 1: Pipeline Architecture - clean vector diagram]
|
||||
90,282 PDFs → VLM Pre-screening → 86,072 PDFs
|
||||
90,282 PDFs → VLM Pre-screening → 86,084 candidates → processing checks → 86,071 PDFs
|
||||
→ YOLOv11 Detection → 182,328 signatures
|
||||
→ ResNet-50 Features → 2048-dim embeddings
|
||||
→ Dual-Descriptor Verification (Cosine + dHash)
|
||||
→ Firm A P7.5-anchored Classifier → Five-way classification
|
||||
→ Pixel-identity + Inter-CPA + Held-Out Firm A validation
|
||||
→ Anchor-Calibrated Five-Way Classifier → Five-way classification
|
||||
→ Pixel-identity Positive Anchor + Inter-CPA Coincidence-Rate Negative Anchor
|
||||
-->
|
||||
|
||||
## B. Data Collection
|
||||
@@ -29,15 +29,16 @@ Each report is a multi-page PDF document containing, among other content, the au
|
||||
CPA names, affiliated accounting firms, and audit engagement tenure were obtained from a publicly available audit-firm tenure registry encompassing 758 unique CPAs across 15 document types, with the majority (86.4%) being standard audit reports.
|
||||
Table I summarizes the dataset composition.
|
||||
|
||||
<!-- TABLE I: Dataset Summary
|
||||
**Table I.** Dataset Summary.
|
||||
|
||||
| Attribute | Value |
|
||||
|-----------|-------|
|
||||
| Total PDF documents | 90,282 |
|
||||
| Date range | 2013–2023 |
|
||||
| Documents with signatures | 86,072 (95.4%) |
|
||||
| Signature-page candidates (VLM-positive) | 86,084 (95.3%) |
|
||||
| Processed for signature extraction | 86,071 (95.3%) |
|
||||
| Unique CPAs identified | 758 |
|
||||
| Accounting firms | >50 |
|
||||
-->
|
||||
|
||||
## C. Signature Page Identification
|
||||
|
||||
@@ -47,8 +48,8 @@ The model was configured with temperature 0 for deterministic output.
|
||||
|
||||
The scanning range was restricted to the first quartile of each document's page count, reflecting the regulatory structure of Taiwanese audit reports in which the auditor's report page is consistently located in the first quarter of the document.
|
||||
Scanning terminated upon the first positive detection.
|
||||
This process identified 86,072 documents with signature pages; the remaining 4,198 documents (4.6%) were classified as having no signatures and excluded.
|
||||
An additional 12 corrupted PDFs were excluded, yielding a final set of 86,071 documents.
|
||||
This process identified 86,084 documents with signature pages; the remaining 4,198 documents (4.6%) were classified as having no signatures and excluded.
|
||||
An additional 13 PDFs that could not be rendered (corruption or read errors) were excluded, yielding a final set of 86,071 documents.
|
||||
|
||||
Cross-validation between the VLM and subsequent YOLO detection confirmed high agreement: YOLO successfully detected signature regions in 98.8% of VLM-positive documents.
|
||||
The 1.2% disagreement reflects the combined rate of (i) VLM false positives (pages incorrectly flagged as containing signatures) and (ii) YOLO false negatives (signature regions missed by the detector), and we do not attempt to attribute the residual to either source without further labeling.
|
||||
@@ -61,20 +62,19 @@ A region was labeled as "signature" if it contained any Chinese handwritten cont
|
||||
|
||||
The model was trained for 100 epochs on a 425/75 training/validation split with COCO pre-trained initialization, achieving strong detection performance (Table II).
|
||||
|
||||
<!-- TABLE II: YOLO Detection Performance
|
||||
**Table II.** YOLO Detection Performance.
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Precision | 0.97–0.98 |
|
||||
| Recall | 0.95–0.98 |
|
||||
| mAP@0.50 | 0.98–0.99 |
|
||||
| mAP@0.50:0.95 | 0.85–0.90 |
|
||||
-->
|
||||
|
||||
Batch inference on all 86,071 documents extracted 182,328 signature images at a rate of 43.1 documents per second (8 workers).
|
||||
A red stamp removal step was applied to each cropped signature using HSV color-space filtering, replacing detected red regions with white pixels to isolate the handwritten content.
|
||||
|
||||
Each signature was matched to its corresponding CPA using positional order (first or second signature on the page) against the official CPA registry, achieving a 92.6% match rate (168,755 of 182,328 signatures).
|
||||
The remaining 7.4% (13,573 signatures) could not be matched to a registered CPA name---typically because the auditor's report page format deviates from the standard two-signature layout, or because OCR of the printed CPA name on the page returns a name not present in the registry---and these signatures are excluded from all subsequent same-CPA pairwise analyses (a same-CPA best-match statistic is undefined when a signature has no assigned CPA). The 92.6% matched subset is the sample that flows into Sections IV-D through IV-H; the unmatched 7.4% are excluded for definitional reasons rather than discarded as noise.
|
||||
Each signature was matched to its corresponding CPA using positional order (first or second signature on the page) against the official CPA registry, achieving a 92.6% match rate (168,755 of 182,328 signatures). The matched records assume standard two-signature ordering; residual order-mismatch risk remains for nonstandard layouts. The remaining 7.4% (13,573 signatures) could not be matched to a registered CPA name---typically because the auditor's report page format deviates from the standard two-signature layout, or because OCR of the printed CPA name on the page returns a name not present in the registry---and these signatures are excluded from all subsequent same-CPA pairwise analyses (a same-CPA best-match statistic is undefined when a signature has no assigned CPA). The 92.6% matched subset forms the candidate pool for same-CPA analyses, before the Big-4 and descriptor-completeness restrictions described in §III-G.
|
||||
|
||||
## E. Feature Extraction
|
||||
|
||||
@@ -84,8 +84,8 @@ The final classification layer was removed, yielding the 2048-dimensional output
|
||||
Preprocessing consisted of resizing to 224×224 pixels with aspect-ratio preservation and white padding, followed by ImageNet channel normalization.
|
||||
All feature vectors were L2-normalized, ensuring that cosine similarity equals the dot product.
|
||||
|
||||
The choice of ResNet-50 without fine-tuning was motivated by three considerations: (1) the task is similarity comparison rather than classification, making general-purpose discriminative features sufficient; (2) ImageNet features have been shown to transfer effectively to document analysis tasks [20], [21]; and (3) avoiding domain-specific fine-tuning reduces the risk of overfitting to dataset-specific artifacts, though we note that a fine-tuned model could potentially improve discriminative performance (see Section V-G).
|
||||
This design choice is validated by an ablation study (Section IV-L) comparing ResNet-50 against VGG-16 and EfficientNet-B0.
|
||||
The choice of ResNet-50 without fine-tuning was motivated by three considerations: (1) the task is similarity comparison rather than classification, making general-purpose discriminative features sufficient; (2) ImageNet features have been shown to transfer effectively to document analysis tasks [20], [21]; and (3) avoiding domain-specific fine-tuning reduces the risk of overfitting to dataset-specific artifacts, though we note that a fine-tuned model could potentially improve discriminative performance (see Section V-H, Engineering-level caveats).
|
||||
This design choice is supported by an ablation study (Section IV-L) comparing ResNet-50 against VGG-16 and EfficientNet-B0.
|
||||
|
||||
## F. Dual-Method Similarity Descriptors
|
||||
|
||||
@@ -105,68 +105,69 @@ Unlike DCT-based perceptual hashes, dHash is computationally lightweight and par
|
||||
|
||||
These descriptors provide partially independent evidence.
|
||||
Cosine similarity is sensitive to the full feature distribution and reflects fine-grained execution variation; dHash captures only coarse perceptual structure and is robust to scanner-induced noise.
|
||||
Non-hand-signing yields extreme similarity under *both* descriptors, since the underlying image is identical up to reproduction noise.
|
||||
Hand-signing, by contrast, yields high dHash similarity (the overall layout of a signature is preserved across writing occasions) but measurably lower cosine similarity (fine execution varies).
|
||||
Non-hand-signing is expected to yield extreme similarity under *both* descriptors, since the underlying image is identical up to reproduction noise; scan-stage noise can in principle push a replicated pair off either extremum but rarely both.
|
||||
One working hypothesis is that some hand-signed repetitions may preserve coarse layout while varying in fine execution, producing relatively higher dHash similarity than cosine similarity within a same-CPA pair; the classifier does not require this hypothesis to hold for all CPAs, and the descriptor-level pattern is used only as input to the deployed rule, not as a within-CPA consistency claim.
|
||||
Convergence of the two descriptors is therefore a natural robustness check; when they disagree, the case is flagged as borderline.
|
||||
|
||||
We did not use SSIM (Structural Similarity Index) [30] or pixel-level comparison as primary descriptors, and the reasons are specific to what each of those measures was designed to do rather than to how either happened to perform on our corpus.
|
||||
We do not use SSIM (Structural Similarity Index) [30] or pixel-level comparison as primary descriptors. SSIM was developed as a perceptual quality index for natural images and is by construction sensitive to the local-luminance and local-contrast perturbations routine in a print-scan cycle (JPEG block artefacts, scan-noise speckle, scanner-rule ghosts) — properties that penalise identically-reproduced signature crops at the very margins SSIM is designed to weight most heavily. Pixel-level distances ($L_1$, $L_2$, pixel-identity counting) are defined on geometrically aligned images at a common resolution and inflate under the sub-pixel offsets that scanner DPI, paper-handling alignment, and PDF-page rasterisation routinely introduce, so two scans of the same physical document cannot score near-identically. The supplementary materials contain the full design-level argument; pixel-identity counting is retained only as a threshold-free positive anchor (§III-K), because byte-identical pairs are necessarily produced by literal file reuse and so do not interact with the alignment-fragility argument.
|
||||
|
||||
SSIM was developed by Wang et al. [30] as a perceptual quality index for *natural images*, and it factorises local-window image statistics into three components---luminance, contrast, and structural correlation---combined multiplicatively over a sliding window.
|
||||
Each of these components is computed at the pixel level on the original-resolution image and is *designed to be sensitive* to small fluctuations in local luminance and local contrast, because that is what makes SSIM track human perception of natural-image quality.
|
||||
Applied to a binarised auditor's signature crop, exactly those design choices become liabilities: the JPEG block artifacts, scan-noise speckle, and faint scanner-rule ghosts that are routine in a print-scan cycle perturb local luminance and local contrast in every window they touch, and SSIM amplifies those perturbations in the structural-correlation product.
|
||||
A signature reproduced twice from the same stored image---the very case that defines our positive class---is therefore one in which SSIM is structurally guaranteed to penalise the easily perturbed margins around the strokes, even though the strokes themselves are identical up to rendering noise.
|
||||
This is a property of how SSIM is constructed, not a finding about how it scored on our data; the empirical observation that the calibration firm exhibits a mean SSIM of only $0.70$ in our corpus is a confirmation of the design-level prediction rather than the basis for the rejection.
|
||||
|
||||
Pixel-level comparison---whether $L_1$, $L_2$, or pixel-identity counting---fails on a stricter design ground.
|
||||
Pixel-level distances are defined on geometrically aligned images at a common resolution, and they treat any sub-pixel translation, rotation, or rescale as a large perturbation by construction (a one-pixel uniform translation flips a fraction of foreground pixels on a thin-stroke signature crop and inflates pixel L1 distance to the same magnitude as for a different signer's signature).
|
||||
Two scans of the same physical document, however, do not share a common pixel grid: scanner DPI, paper-handling alignment, and PDF-page rasterisation each contribute random sub-pixel offsets, and the print-scan cycle that intervenes between the stored stamp image and the audit-report PDF additionally introduces resolution mismatch and small geometric drift.
|
||||
A pixel-level descriptor cannot therefore satisfy the basic stability requirement for our task: two presentations of the same stored image must score nearly identically.
|
||||
We retain pixel-identity counting only as a *threshold-free anchor* (Section III-K), because byte-identical pairs in our corpus are necessarily produced by literal file reuse rather than by repeated scanning, and so they do not interact with the alignment-fragility argument; they are not used as a primary similarity descriptor.
|
||||
|
||||
Cosine similarity on deep embeddings and dHash, in contrast, both remain stable across the print-scan-rasterise cycle by design: cosine on L2-normalised pooled features is invariant to overall scale and bias and degrades gracefully under local-pixel noise that the convolutional backbone has been trained to absorb [14], [21], while dHash compresses the image to a $9 \times 8$ grayscale grid before computing horizontal-gradient signs, which removes the resolution and sub-pixel-alignment sensitivity that breaks pixel-level comparison [19], [27].
|
||||
Together they constitute the dual descriptor used throughout the rest of this paper.
|
||||
Cosine similarity on L2-normalised deep embeddings and dHash both remain stable across the print-scan-rasterise cycle by design [14], [19], [21], [27]; together they constitute the dual descriptor used throughout the rest of this paper.
|
||||
|
||||
## G. Unit of Analysis and Scope
|
||||
|
||||
We analyse signatures at two units of resolution. The **signature** — one signature image extracted from one report — is the operational unit of classification (§III-L) and of the signature-level analyses in §IV (notably §IV-J for the five-way per-signature category counts and the inherited inter-CPA negative-anchor coincidence-rate analysis referenced in §IV-I; reported under prior "FAR" terminology in v3.x). The **accountant** — one CPA aggregated over all of their signatures in the corpus — is the unit of mixture-model characterisation (§III-J), of per-CPA internal-consistency analysis (§III-K), and of the leave-one-firm-out reproducibility check (§III-K). At the accountant level we compute, for each CPA with $n_{\text{sig}} \geq 10$ signatures, the per-CPA mean of the per-signature best-match cosine ($\overline{\text{cos}}_a$) and the per-CPA mean of the independent-minimum dHash ($\overline{\text{dHash}}_a$). The minimum threshold of 10 signatures per CPA is required for the per-CPA mean to be a stable summary; CPAs below this threshold are excluded from the accountant-level analyses but remain in the per-signature analyses.
|
||||
We analyse signatures at two **descriptor-summary** units of resolution. The **signature** — one signature image extracted from one report — is the operational unit of classification (§III-H.1) and of the signature-level analyses in §IV (notably §IV-J for the five-way per-signature category counts and the inter-CPA negative-anchor coincidence-rate analysis referenced in §IV-I). The **accountant** — one CPA aggregated over all of their signatures in the corpus — is the unit of mixture-model characterisation (§III-J), of per-CPA internal-consistency analysis (§III-K), and of the leave-one-firm-out reproducibility check (§III-K). At the accountant level we compute, for each CPA with $n_{\text{sig}} \geq 10$ signatures, the per-CPA mean of the per-signature best-match cosine ($\overline{\text{cos}}_a$) and the per-CPA mean of the independent-minimum dHash ($\overline{\text{dHash}}_a$). The minimum threshold of 10 signatures per CPA is required for the per-CPA mean to be a stable summary; CPAs below this threshold are excluded from the accountant-level analyses but remain in the per-signature analyses. §III-L additionally characterises the deployed rule's behaviour at three **operational reporting** units (per-comparison, per-signature, per-document), which are distinct from the descriptor-summary units defined here: the descriptor-summary units summarise input descriptors; the operational reporting units summarise rule outputs.
|
||||
|
||||
We make no within-year or across-year uniformity assumption about CPA signing mechanisms. Per-signature labels are signature-level quantities throughout this paper; we do not translate them to per-report or per-partner mechanism assignments, and we abstain from partner-level frequency inferences (such as "X% of CPAs hand-sign") that would require such a translation. A CPA's per-CPA mean is a *summary statistic* of their observed signatures, not a claim that all of their signatures share a single mechanism.
|
||||
|
||||
We adopt one stipulation about same-CPA pair detectability:
|
||||
|
||||
> **(A1) Pair-detectability.** *If a CPA uses image replication anywhere in the corpus, then at least one same-CPA signature pair is near-identical (after reproduction noise) within the cross-year same-CPA pool used by the max-cosine / min-dHash computation.*
|
||||
> **(A1) Pair-detectability.** *If a CPA uses image replication anywhere in the corpus, then at least one same-CPA signature pair is near-identical (after reproduction noise) within the observed same-CPA candidate pool used by the max-cosine / min-dHash computation, pooled over the CPA's reports across years. A1 does not assume temporal stability of handwriting or scanning workflow within or across years.*
|
||||
|
||||
A1 is plausible for high-volume stamping or firm-level electronic signing workflows but is not guaranteed when (i) the corpus contains only one observed replicated report for a CPA, (ii) multiple template variants are used in parallel, or (iii) scan-stage noise pushes a replicated pair outside the detection regime. A1 is the only assumption the per-signature detector requires to be sensitive to replication.
|
||||
|
||||
**Scope: the Big-4 sub-corpus.** v4.0's primary analyses (§III-I, §III-J, §III-K, §III-L, and the v4-new analyses in §IV-D through §IV-J) are restricted to the four largest accounting firms in Taiwan, pseudonymously labelled Firm A through Firm D throughout the manuscript. §IV-A through §IV-C, §IV-I (inter-CPA negative-anchor coincidence rate), and §IV-L (feature-backbone ablation) report inherited corpus-wide v3.x material that v4.0 does not re-scope to Big-4. §IV-K reports a deliberately narrow full-dataset cross-check at $n = 686$ CPAs. The Big-4 sub-corpus comprises 437 CPAs (171 / 112 / 102 / 52 across Firms A through D) with $n_{\text{sig}} \geq 10$ — the threshold for accountant-level analyses (Scripts 36, 38) — totalling 150,442 Big-4 signatures with both pre-computed descriptors available. Restricting the v4-new analyses to Big-4 is a methodological choice driven by four considerations:
|
||||
**Scope: the Big-4 sub-corpus.** The primary analyses (§III-I, §III-J, §III-K, §III-L, and the corresponding §IV-D through §IV-J and §IV-M tables) are restricted to the four largest accounting firms in Taiwan, pseudonymously labelled Firm A through Firm D throughout the manuscript. §IV-A through §IV-C and §IV-L report the corpus-wide pipeline performance and feature-backbone ablation that support the descriptor choice of §III-F; §IV-K reports a deliberately narrow full-dataset cross-check at $n = 686$ CPAs. The Big-4 sub-corpus comprises 437 CPAs (171 / 112 / 102 / 52 across Firms A through D) with $n_{\text{sig}} \geq 10$ — the threshold for accountant-level analyses — totalling 150,442 Big-4 signatures with both pre-computed descriptors available. Restricting the primary analyses to Big-4 is a methodological choice driven by four considerations:
|
||||
|
||||
1. **Leave-one-firm-out fold feasibility.** §III-K reports leave-one-firm-out (LOOO) cross-validation of the Big-4 K=3 fit. The Big-4 sub-corpus permits a four-fold LOOO at the firm level (one fold per Big-4 firm). No analogous firm-level fold is available outside Big-4 because mid/small firms have CPA counts of $O(1)$–$O(30)$ per firm.
|
||||
1. **Restricted generalisability claim and Big-4 institutional comparability.** The primary claims are scoped to the Big-4 audit-report context, where the four firms share comparable institutional scale, document-production infrastructure, and CPA-volume regime; we do not assert that the same descriptive mixture structure or operational alert behaviour extends to mid/small firms. The 249 non-Big-4 CPAs enter only (a) as an external reference population in §III-H.2's reverse-anchor internal-consistency check, (b) as a robustness comparison in §IV-K, and (c) as a corroborating-population check on the dHash discrete-mass-point artefact in §III-I.4. Generalisation beyond Big-4 is left as future work.
|
||||
|
||||
2. **Firm A as templated-end case study.** Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the descriptor plane (§III-J K=3 component cross-tab; v3.x byte-level pair analysis referenced in §III-H). v4.0 retains Firm A within the Big-4 scope as a descriptive case study of the templated end, rather than treating Firm A as the calibration anchor for thresholds (the v3.x role of Firm A).
|
||||
2. **Within-firm cross-CPA collision structure analysis.** §III-L.4 reports a Big-4 cross-firm hit-matrix analysis that quantifies the within-firm cross-CPA template-like collision pattern. The four-firm setting affords the cleanest signal for this analysis; replicating the same matrix structure on the heterogeneous mid/small-firm tail is left as future work.
|
||||
|
||||
3. **Within-firm cross-CPA collision structure analysis.** §III-L.4 reports a Big-4 cross-firm hit-matrix analysis (Script 44) that quantifies the within-firm cross-CPA template-like collision pattern. The four-firm setting affords the cleanest signal for this analysis; replicating the same matrix structure on the heterogeneous mid/small-firm tail is left as future work.
|
||||
3. **Firm A as templated-end case study.** Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the descriptor plane (§III-J K=3 component cross-tab; byte-level pair analysis referenced in §III-H.2). We retain Firm A within the Big-4 scope as a descriptive case study of the templated end rather than as the calibration anchor for thresholds.
|
||||
|
||||
4. **Restricted generalisability claim.** v4.0's primary claims are scoped to the Big-4 audit-report context; we do not assert that the same descriptive mixture structure or operational alert behaviour extends to mid/small firms. The 249 non-Big-4 CPAs enter only (a) as an external reference population in §III-H's reverse-anchor internal-consistency check, (b) as a robustness comparison in §IV-K, and (c) as a corroborating-population check on the dHash discrete-mass-point artefact in §III-I.4 (Script 39c). Generalisation beyond Big-4 is left as future work.
|
||||
|
||||
We earlier (v4.0 first draft) listed "statistical multimodality at the accountant level" among the scope justifications, on the basis that the Hartigan dip test rejects unimodality on the Big-4 accountant-level marginals. §III-I.4 reports diagnostics (Scripts 39b–39e) that explain the rejection as a joint effect of between-firm composition shift and dHash integer mass points, not as evidence of within-population continuous bimodality. We therefore no longer list dip-test multimodality among the Big-4 scope rationales; the K=3 mixture is retained as a descriptive partition (§III-J), not as inferential evidence for two mechanism modes.
|
||||
4. **Leave-one-firm-out fold feasibility.** §III-K reports leave-one-firm-out (LOOO) cross-validation of the Big-4 K=3 fit. The Big-4 sub-corpus permits a four-fold LOOO at the firm level (one fold per Big-4 firm). No analogous firm-level fold is available outside Big-4 because mid/small firms have CPA counts of $O(1)$–$O(30)$ per firm.
|
||||
|
||||
**Sample-size reconciliation.** Two Big-4 signature counts appear in this section and §IV: $n = 150{,}442$ for analyses using the pre-computed per-signature descriptors $\text{cos}_s$ (`max_similarity_to_same_accountant`) and $\text{dHash}_s$ (`min_dhash_independent`), and $n = 150{,}453$ for analyses recomputing pair-level metrics directly from the stored feature and dHash byte vectors (Scripts 40b, 43, 44). The $11$-signature difference reflects descriptor-completion status: $11$ signatures have feature vectors and dHash byte vectors stored but lack the pre-computed extrema. The $11$ signatures are negligible at population scale and do not affect any reported coincidence rate within $0.01$ percentage point. The CPA counts $468$ (all Big-4 CPAs with both vectors stored) and $437$ (Big-4 CPAs with $n_{\text{sig}} \geq 10$ for accountant-level stability) likewise reflect a single uniform exclusion rule rather than analysis-specific subsetting.
|
||||
|
||||
## H. Reference Populations
|
||||
## H. Operational Classifier and Reference Populations
|
||||
|
||||
v4.0 distinguishes two reference populations in its calibration, replacing v3.x's single-anchor framing.
|
||||
### H.1. Deployed Operational Rule
|
||||
|
||||
**Internal reference: Firm A as the templated-end case study.** Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the Big-4 descriptor plane. In the Big-4 K=3 descriptive partition (§III-J; Scripts 35, 38), Firm A accounts for 0% of the C1 component (low-cos / high-dHash corner; cos $\approx 0.946$, dHash $\approx 9.17$, weight $\approx 0.143$), 17.5% of the C2 component (central region), and 82.5% of the C3 component (high-cos / low-dHash corner); the opposite pattern holds at Firm C (Script 35: 23.5% C1, 75.5% C2, 1.0% C3, hereafter referred to as "the Firm whose CPAs are most concentrated in C1"). The byte-level pair analysis reported in v3.x §IV-F.1 identifies 145 Firm A pixel-identical signatures at the signature level (Script 40 verifies the 145/262 split among Big-4 pixel-identical signatures); the additional details that v3.x attributes to this analysis (50 distinct Firm A partners of 180 registered; 35 byte-identical matches spanning different fiscal years) are inherited from the Script 28 / Appendix B byte-decomposition output and were not regenerated in the v4.0 spike scripts. We retain those v3.x details by reference and mark them in the provenance table as "inherited from v3 §IV-F.1 / Script 28."
|
||||
Each Big-4 signature is assigned to one of five categories using the per-signature descriptor pair $(\text{cos}_s, \text{dHash}_s)$ where $\text{cos}_s$ is the maximum cosine similarity to another signature by the same CPA and $\text{dHash}_s$ is the minimum independent dHash to another signature by the same CPA. The five labels below name regions of the descriptor space and are operational rule outputs, not validated ground-truth classes; the label names reflect the screening hypothesis associated with each region and are subject to the unsupervised-setting caveats of §III-M:
|
||||
|
||||
In v4.0, Firm A is *not* the calibration anchor for the operational threshold. Firm A enters the Big-4 mixture on equal footing with Firms B through D; the K=3 components are derived from the joint Big-4 distribution (§III-J), not from Firm A alone. Firm A's role in the methodology is descriptive: it is the Big-4 firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the descriptor plane, and the byte-level pair evidence above provides the firm-level signature-reuse evidence that anchors §III-K's pixel-identity positive-anchor miss rate.
|
||||
1. **High-confidence non-hand-signed (HC):** Cosine $> 0.95$ AND $\text{dHash}_{\text{indep}} \leq 5$. Both descriptors converge on image-similarity evidence consistent with replication; mechanism attribution remains subject to §III-M.
|
||||
2. **Moderate-confidence non-hand-signed (MC):** Cosine $> 0.95$ AND $5 < \text{dHash}_{\text{indep}} \leq 15$. Feature-level similarity is strong; structural similarity is present but below the high-confidence cutoff.
|
||||
3. **High style consistency (HSC):** Cosine $> 0.95$ AND $\text{dHash}_{\text{indep}} > 15$. High feature-level similarity without structural corroboration; the descriptor signature is operationally distinguished from HC/MC, but the underlying mechanism (within-CPA signing style, lossy image reproduction with structural drift, or a hybrid) is not resolved by descriptor data alone.
|
||||
4. **Uncertain (UN):** Cosine between the all-pairs intra/inter KDE crossover ($0.837$) and $0.95$.
|
||||
5. **Likely hand-signed (LH):** Cosine $\leq 0.837$. The "Likely hand-signed" name reflects the screening hypothesis that low maximum same-CPA cosine similarity is more consistent with hand-signing variation than with image replication; the label is operational, not a verified hand-signed classification, since cross-year handwriting drift, scanner-workflow change, or template variant rotation within a CPA's reports can also yield a low max-cosine within a same-CPA pool.
|
||||
|
||||
**External reference: non-Big-4 as the reverse-anchor reference for internal-consistency checking.** The 249 non-Big-4 CPAs ($n_{\text{sig}} \geq 10$, drawn from $\sim$30 mid- and small-firms) constitute a population strictly outside the Big-4 target. Their per-CPA $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ distribution defines a 2D Gaussian reference (fit by Minimum Covariance Determinant with support fraction 0.85 for robustness; Script 38). This reference is used in §III-K's reverse-anchor internal-consistency check: each Big-4 CPA's location relative to the reference centre, measured as the marginal cosine cumulative-distribution-function value under the reference, is one of three feature-derived scores that v4.0 uses as a cross-check on the inherited per-signature classifier. The reverse-anchor reference is *not* a positive or negative anchor for threshold derivation — its role is to provide a strictly out-of-target benchmark against which the within-Big-4 mixture-derived ranking can be internally cross-checked.
|
||||
Document-level labels are aggregated via the worst-case rule: each audit report inherits the most-replication-consistent category among its certifying-CPA signatures (rank order HC > MC > HSC > UN > LH). The thresholds ($\text{cos} = 0.95$ as the cosine operating point, $\text{cos} = 0.837$ as the all-pairs KDE crossover, $\text{dHash} = 5$ and $15$ as structural-similarity sub-band cutoffs) retain their prior calibration provenance (see supplementary materials). These thresholds define the deployed screening rule; the present analysis does not re-derive them as optimal cutoffs but characterises their behaviour under inter-CPA coincidence anchors (developed in §III-L).
|
||||
|
||||
The remainder of this section (§III-H.2) describes the reference populations used to calibrate and cross-check this rule. §III-I demonstrates that the descriptor distributions do not provide a within-population natural threshold; §III-J–§III-K develop the descriptive partition and internal-consistency cross-checks; §III-L develops the anchor-based threshold calibration; §III-M discloses the unsupervised-setting limits.
|
||||
|
||||
### H.2. Reference Populations
|
||||
|
||||
The supporting diagnostics use two reference populations: Firm A as a within-Big-4 templated-end case study, and the 249 non-Big-4 CPAs as an out-of-target reference for internal-consistency checking. Neither population is the calibration anchor for the deployed threshold; both are descriptive references that inform the cross-checks in §III-K.
|
||||
|
||||
**Internal reference: Firm A as the templated-end case study.** Firm A is empirically the firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the Big-4 descriptor plane. In the Big-4 K=3 descriptive partition (§III-J; Scripts 35, 38), Firm A accounts for 0% of the C1 component (low-cos / high-dHash corner; cos $\approx 0.946$, dHash $\approx 9.17$, weight $\approx 0.143$), 17.5% of the C2 component (central region), and 82.5% of the C3 component (high-cos / low-dHash corner); the opposite pattern holds at Firm C (Script 35: 23.5% C1, 75.5% C2, 1.0% C3, hereafter referred to as "the Firm whose CPAs are most concentrated in C1"). Byte-level decomposition of these signatures (see supplementary materials) identifies 145 Firm A pixel-identical signatures, spanning 50 distinct Firm A partners of the 180 registered, with 35 byte-identical matches occurring across different fiscal years; the 145 are the Firm A portion of the 262 byte-identical Big-4 signatures.
|
||||
|
||||
Firm A is *not* the calibration anchor for the operational threshold. Firm A enters the Big-4 mixture on equal footing with Firms B through D; the K=3 components are derived from the joint Big-4 distribution (§III-J), not from Firm A alone. Firm A's role in the methodology is descriptive: it is the Big-4 firm whose CPAs are most concentrated in the high-cosine, low-dHash corner of the descriptor plane, and the byte-level pair evidence above provides the firm-level signature-reuse evidence that anchors §III-K's pixel-identity positive-anchor miss rate.
|
||||
|
||||
**External reference: non-Big-4 as the reverse-anchor reference for internal-consistency checking.** The 249 non-Big-4 CPAs ($n_{\text{sig}} \geq 10$, drawn from $\sim$30 mid- and small-firms) constitute a population strictly outside the Big-4 target. Their per-CPA $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ distribution defines a 2D Gaussian reference (fit by Minimum Covariance Determinant with support fraction 0.85 for robustness; Script 38). This reference is used in §III-K's reverse-anchor internal-consistency check: each Big-4 CPA's location relative to the reference centre, measured as the marginal cosine cumulative-distribution-function value under the reference, is one of three feature-derived scores used as a cross-check on the per-signature classifier. The reverse-anchor reference is *not* a positive or negative anchor for threshold derivation — its role is to provide a strictly out-of-target benchmark against which the within-Big-4 mixture-derived ranking can be internally cross-checked.
|
||||
|
||||
The reverse-anchor reference centre is at $\overline{\text{cos}} = 0.935$, $\overline{\text{dHash}} = 9.77$ (Script 38). The reference sits at a lower cosine and higher dHash than the Big-4 K=3 low-cos / high-dHash component (cos $= 0.946$, dHash $= 9.17$; §III-J); compared to the Big-4 high-cos / low-dHash component (cos $= 0.983$, dHash $= 2.41$; §III-J) the reference is markedly less replication-dominated. The reverse-anchor metric for a given Big-4 CPA is the percentile of $\overline{\text{cos}}_a$ within the reference marginal cosine distribution, sign-flipped so that lower percentile (further into the left tail of the reference) corresponds to a Big-4 CPA whose mean cosine sits further from the templated end of the descriptor plane. This is a "deviation in the less-replication-dominated descriptor-position direction" measure, not a "deviation toward the templated descriptor-position" measure; the reference is the less-replication-dominated population.
|
||||
|
||||
## I. Distributional Diagnostics: Why the Composition Path Does Not Yield a Natural Threshold
|
||||
|
||||
This section characterises the joint distribution of accountant-level descriptor means $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ across the 437 Big-4 CPAs of §III-G and tests whether the distribution provides distributional support — in the form of within-population bimodality — for the operational thresholds inherited from v3.x. We apply four diagnostic procedures in turn: a univariate unimodality test on each accountant-level marginal; a 2D Gaussian mixture fit (developed in §III-J); a density-smoothness diagnostic; and a composition decomposition that distinguishes within-population multimodality from between-firm location-shift artefacts (the v4-new diagnostic battery). The four diagnostics jointly imply that the operational thresholds are *not* anchored by distributional bimodality: §III-L develops an anchor-based calibration framework that does not require this assumption.
|
||||
This section characterises the joint distribution of accountant-level descriptor means $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ across the 437 Big-4 CPAs of §III-G and tests whether the distribution provides distributional support — in the form of within-population bimodality — for the deployed operational thresholds. We apply four diagnostic procedures in turn: a univariate unimodality test on each accountant-level marginal; a 2D Gaussian mixture fit (developed in §III-J); a density-smoothness diagnostic; and a composition decomposition that distinguishes within-population multimodality from between-firm location-shift artefacts. The four diagnostics jointly imply that the operational thresholds are *not* anchored by distributional bimodality: §III-L develops an anchor-based calibration framework that does not require this assumption.
|
||||
|
||||
**1. Hartigan dip test on each accountant-level marginal.** We apply the Hartigan & Hartigan dip test [37] to each of the two marginal distributions $\{\overline{\text{cos}}_a\}_{a=1}^{437}$ and $\{\overline{\text{dHash}}_a\}_{a=1}^{437}$, with bootstrap-based $p$-value estimation ($n_{\text{boot}} = 2000$). In both cases no bootstrap replicate exceeded the observed dip statistic, so the empirical $p$-value is bounded above by $5 \times 10^{-4}$; we report this in tables as $p < 5 \times 10^{-4}$ rather than $p = 0$ to reflect the bootstrap resolution (Script 34). For comparison, no rejection of unimodality holds in the comparison scopes tested in Script 32: Firm A pooled alone ($p_{\text{cos}} = 0.992$, $p_{\text{dHash}} = 0.924$, $n = 171$); Firms B + C + D pooled ($p_{\text{cos}} = 0.998$, $p_{\text{dHash}} = 0.906$, $n = 266$); all non-Firm-A CPAs pooled ($p_{\text{cos}} = 0.998$, $p_{\text{dHash}} = 0.907$, $n = 515$). Single-firm dip tests for Firms B, C, and D were not separately computed; the comparison scopes above sufficed to establish that no narrower-than-Big-4 *tested* scope at the accountant level rejected unimodality. The accountant-level Big-4 rejection is a descriptive observation; §III-I.4 below shows that the rejection is fully explained by between-firm location-shift effects rather than within-population bimodality.
|
||||
|
||||
@@ -178,7 +179,7 @@ This section characterises the joint distribution of accountant-level descriptor
|
||||
|
||||
*Within-firm signature-level dip (Scripts 39b, 39c).* Repeating the dip test at the signature level inside each individual Big-4 firm (Script 39b) and inside each individual non-Big-4 firm with $\geq 500$ signatures (Script 39c) yields a consistent picture. The cosine marginal *fails* to reject unimodality in every single firm tested — all four Big-4 firms ($p_{\text{cos}} \in \{0.176, 0.991, 0.551, 0.976\}$ for Firms A through D; Script 39b) and ten non-Big-4 firms with $\geq 500$ signatures ($p_{\text{cos}} \in [0.59, 0.99]$; Script 39c). The raw dHash marginal *does* reject unimodality in every firm tested ($p < 5 \times 10^{-4}$ in all $14$ firms), but the raw dHash values are integer-valued in $\{0, 1, \ldots, 64\}$, leaving open the possibility of an integer-tie artefact.
|
||||
|
||||
*Integer-jitter robustness (Scripts 39d, 39e).* Adding independent uniform jitter $\sim \mathrm{U}[-0.5, +0.5]$ to break exact dHash ties and re-running the dip test on the perturbed signature cloud (5 seeds, $n_{\text{boot}} = 2000$; Script 39d) eliminates the dHash within-firm rejection in every Big-4 firm tested (Firm A jittered $p_{\text{median}} = 0.999$; B $0.996$; C $0.999$; D $0.9995$; $0$/$5$ seeds reject at $\alpha = 0.05$ in any firm). A codex-verified read-only spike applying the same jitter procedure to the ten non-Big-4 firms with $\geq 500$ signatures (Script 39c substrate) likewise yields no rejection ($0$/$10$ firms reject at $\alpha = 0.05$; per-firm median-$p$ range $[0.38, 1.00]$). The pooled-Big-4 dHash dip *does* survive jitter alone ($p_{\text{median}} = 0$, $5$/$5$ seeds reject), but Firm A's mean dHash ($2.73$) is substantially below Firms B/C/D's ($6.46$, $7.39$, $7.21$) — a between-firm location shift. Script 39e applies a 2 \times 2 factorial correction (firm-mean centring $\times$ integer jitter) on the Big-4 pooled dHash:
|
||||
*Integer-jitter robustness (Scripts 39d, 39e).* Adding independent uniform jitter $\sim \mathrm{U}[-0.5, +0.5]$ to break exact dHash ties and re-running the dip test on the perturbed signature cloud (5 seeds, $n_{\text{boot}} = 2000$; Script 39d) eliminates the dHash within-firm rejection in every Big-4 firm tested (Firm A jittered $p_{\text{median}} = 0.999$; B $0.996$; C $0.999$; D $0.9995$; $0$/$5$ seeds reject at $\alpha = 0.05$ in any firm). The pooled-Big-4 dHash dip *does* survive jitter alone ($p_{\text{median}} = 0$, $5$/$5$ seeds reject), but Firm A's mean dHash ($2.73$) is substantially below Firms B/C/D's ($6.46$, $7.39$, $7.21$) — a between-firm location shift. Script 39e applies a 2 \times 2 factorial correction (firm-mean centring $\times$ integer jitter) on the Big-4 pooled dHash:
|
||||
|
||||
| Condition | Firm-mean centred | Integer jitter | Median dip $p$ | Reject at $\alpha = 0.05$ |
|
||||
|---|---|---|---|---|
|
||||
@@ -189,15 +190,15 @@ This section characterises the joint distribution of accountant-level descriptor
|
||||
|
||||
Removing *both* the between-firm location shift *and* the integer mass points eliminates the Big-4 dHash rejection. The Big-4 pooled dHash multimodality is therefore fully attributable to firm-composition contrast (primarily Firm A's mean $\text{dHash} = 2.73$ versus Firms B/C/D $\approx 6.5$–$7.4$) and integer-density artefacts, with no residual continuous within-firm bimodality.
|
||||
|
||||
*Cosine analogue.* The cosine axis exhibits the same pattern. Codex-verified read-only spike on the Big-4 pooled signature cloud yields signature-level cosine dip $p < 5 \times 10^{-4}$ on the raw data, but $p = 0.597$ after firm-mean centring; accountant-level cosine $p = 1.0$ after firm-mean centring. The cosine multimodality is therefore between-firm composition-driven, not within-population bimodality.
|
||||
*Cosine analogue.* The cosine axis follows the same pattern by construction: the within-firm signature-level cosine dip tests above (Scripts 39b, 39c) fail to reject in every Big-4 firm and in every eligible non-Big-4 firm, so any pooled cosine multimodality must arise from between-firm composition rather than from within-population bimodality.
|
||||
|
||||
*Integer-histogram valleys (Script 39d).* A genuine within-firm dHash antimode would appear as a strict local minimum in the count histogram with deep relative depth. Within each of the four Big-4 firms, the dHash histogram on bins $0$–$20$ exhibits no strict local minimum; the Big-4 pooled histogram exhibits one shallow valley at $\text{dHash} = 4$ with relative depth $0.021$ (a $2.1\%$ count drop). No valley near the inherited $\text{dHash} = 5$ operational boundary appears within any individual firm. The hypothesised dHash antimode near $\text{dHash} \approx 5$ is not empirically supported by the histogram analysis.
|
||||
*Integer-histogram valleys (Script 39d).* A genuine within-firm dHash antimode would appear as a strict local minimum in the count histogram with deep relative depth. Within each of the four Big-4 firms, the dHash histogram on bins $0$–$20$ exhibits no strict local minimum; the Big-4 pooled histogram exhibits one shallow valley at $\text{dHash} = 4$ with relative depth $0.021$ (a $2.1\%$ count drop). No valley near the deployed $\text{dHash} = 5$ operational boundary appears within any individual firm. The hypothesised dHash antimode near $\text{dHash} \approx 5$ is not empirically supported by the histogram analysis.
|
||||
|
||||
**5. Conclusion: no natural threshold from the descriptor distribution.** §III-I.4 jointly establishes that (a) the Big-4 accountant-level dip rejection is fully attributable to between-firm composition and integer mass-point artefacts; (b) within any individual firm, the descriptor marginals at the signature level are unimodal once integer ties are broken; and (c) no integer-histogram valley near the inherited $\text{dHash} = 5$ operational boundary exists within any firm. The descriptor distributions therefore do not contain a within-population bimodal antimode that could anchor an operational threshold. The K=2 / K=3 mixture fits of §III-I.2 and §III-J are retained as *descriptive partitions* that reflect firm-composition contrast, not as inferential evidence for two or three population modes. §III-L develops the v4.0 anchor-based threshold calibration framework, which derives operational rates from inter-CPA pair-level negative-anchor coincidences rather than from a distributional antimode.
|
||||
**5. Conclusion: no natural threshold from the descriptor distribution.** §III-I.4 jointly establishes that (a) the Big-4 accountant-level dip rejection is fully attributable to between-firm composition and integer mass-point artefacts; (b) within the Big-4 firms, the descriptor marginals at the signature level are unimodal once integer ties are broken (Scripts 39b, 39d); (c) eligible non-Big-4 checks provide corroborating raw-axis evidence on the cosine dimension (Script 39c) and corroborate the integer-mass-point reading of raw dHash, but are not used as calibration evidence for the deployed thresholds; and (d) no integer-histogram valley near the deployed $\text{dHash} = 5$ operational boundary exists within any Big-4 firm. The descriptor distributions therefore do not contain a within-population bimodal antimode that could anchor an operational threshold. The K=2 / K=3 mixture fits of §III-I.2 and §III-J are retained as *descriptive partitions* that reflect firm-composition contrast, not as inferential evidence for two or three population modes. §III-L develops the anchor-based threshold calibration framework, which derives operational rates from inter-CPA pair-level negative-anchor coincidences rather than from a distributional antimode.
|
||||
|
||||
## J. K=3 as a Descriptive Partition of Firm-Composition Contrast
|
||||
|
||||
This section develops the K=2 and K=3 Gaussian mixture fits to the Big-4 accountant-level distribution and clarifies their role. **Both fits are descriptive partitions of the joint Big-4 distribution; they reflect firm-composition contrast — primarily Firm A versus Firms B, C, D — rather than within-population mechanism modes.** §III-I.4 demonstrates that the apparent multimodality of the accountant-level marginals is fully explained by between-firm location shifts and integer mass-point artefacts, leaving no residual evidence for two or three latent within-population mechanism classes. Neither mixture is used to assign signature-level or document-level labels in the v4.0 primary analysis. The operational classifier of §III-L is calibrated via inter-CPA negative-anchor coincidence rates, not via mixture-derived antimodes.
|
||||
This section develops the K=2 and K=3 Gaussian mixture fits to the Big-4 accountant-level distribution and clarifies their role. **Both fits are descriptive partitions of the joint Big-4 distribution; they reflect firm-composition contrast — primarily Firm A versus Firms B, C, D — rather than within-population mechanism modes.** §III-I.4 demonstrates that the apparent multimodality of the accountant-level marginals is fully explained by between-firm location shifts and integer mass-point artefacts, leaving no residual evidence for two or three latent within-population mechanism classes. Neither mixture is used to assign signature-level or document-level labels in the primary analysis. The operational classifier of §III-H.1 is calibrated in §III-L via inter-CPA negative-anchor coincidence rates, not via mixture-derived antimodes.
|
||||
|
||||
**K=2 fit.** Two components at $(\overline{\text{cos}}, \overline{\text{dHash}}) = (0.954, 7.14)$ (weight $0.689$) and $(0.983, 2.41)$ (weight $0.311$) (Script 34). $\text{BIC}(K{=}2) = -1108.45$. Marginal crossings: $\overline{\text{cos}}^* = 0.9755$, $\overline{\text{dHash}}^* = 3.755$. We refer to the components by index rather than by mechanism labels, since §III-I.4 establishes that the K=2 separation is firm-compositional rather than mechanistic.
|
||||
|
||||
@@ -209,7 +210,7 @@ This section develops the K=2 and K=3 Gaussian mixture fits to the Big-4 account
|
||||
| C2 | 0.9558 | 6.66 | 0.536 | central region |
|
||||
| C3 | 0.9826 | 2.41 | 0.321 | high-cos / low-dHash corner |
|
||||
|
||||
$\text{BIC}(K{=}3) = -1111.93$, lower than $K{=}2$ by $3.48$ (mild numerical preference for K=3 under standard BIC interpretation, but not by itself decisive). The "descriptive position" column replaces v3.x's "hand-leaning / mixed / replicated" mechanism labels: §III-I.4 establishes that the cosine and dHash axes both lack within-population bimodality, so component centres are best interpreted as locations in a continuous descriptor space rather than as latent mechanism modes.
|
||||
$\text{BIC}(K{=}3) = -1111.93$, lower than $K{=}2$ by $3.48$ (mild numerical preference for K=3 under standard BIC interpretation, but not by itself decisive). The "descriptive position" column refrains from any mechanism interpretation: §III-I.4 establishes that the cosine and dHash axes both lack within-population bimodality, so component centres are best interpreted as locations in a continuous descriptor space rather than as latent mechanism modes.
|
||||
|
||||
**Per-firm component composition (Script 35 firm × cluster cross-tab).** The K=3 partition is dominated by firm membership:
|
||||
|
||||
@@ -222,23 +223,23 @@ Firm A accounts for $141$ of the $143$ C3-assigned CPAs; Firm C accounts for $24
|
||||
|
||||
**Leave-one-firm-out stability (Scripts 36, 37).** Leave-one-firm-out cross-validation shows that K=2 is unstable across folds: holding Firm A out gives a fold rule cos $> 0.938$ AND dHash $\leq 8.79$, while holding any single non-Firm-A Big-4 firm out gives a fold rule near cos $> 0.975$ AND dHash $\leq 3.76$ (Script 36). The maximum absolute deviation of the four fold cosine crossings from their across-fold mean is $0.028$ (the corresponding pairwise across-fold range is $0.0376$, from $0.9380$ for the held-out-Firm-A fold to $0.9756$ for the held-out-Firm-D fold; Script 36 stability summary). The $0.028$ value is $5.6\times$ the report's $0.005$ across-fold stability tolerance. K=3 in contrast has a *reproducible component shape*: across the four folds the C1 cosine mean varies by at most $0.005$, the C1 dHash mean by at most $0.96$, and the C1 weight by at most $0.023$ (Script 37). K=3 hard-posterior membership for the held-out firm is composition-sensitive — for Firm C the held-out C1 rate is $36.3\%$ vs the full-Big-4 baseline of $23.5\%$, an absolute difference of $12.8$ pp; for Firm A the held-out C1 rate is $4.7\%$ vs baseline $0.0\%$; the report's own legend classifies this pattern as `P2_PARTIAL` ("the C1 cluster exists but membership is not well-predicted by the held-out fit"). We accordingly do not use K=3 hard-posterior membership as an operational label.
|
||||
|
||||
We take the joint K=2 / K=3 LOOO evidence as supporting the following descriptive claims, all of which are used in §III-K and §V but none of which underwrites the v4.0 operational classifier:
|
||||
We take the joint K=2 / K=3 LOOO evidence as supporting the following descriptive claims, all of which are used in §III-K and §V but none of which underwrites the operational classifier:
|
||||
|
||||
- The Big-4 K=2 marginal crossing $(0.975, 3.76)$ is essentially a firm-mass separator between Firm A and Firms B + C + D, not a within-Big-4 mechanism boundary.
|
||||
- The Big-4 K=3 mixture exhibits a reproducible three-component component shape across LOOO folds at the descriptor-position level, with C1 reproducibly located at $\overline{\text{cos}} \approx 0.946$, $\overline{\text{dHash}} \approx 9.17$.
|
||||
- Hard-posterior K=3 membership is composition-sensitive across folds (max absolute deviation $12.8$ pp); K=3 is therefore not used to assign operational labels to CPAs in v4.0.
|
||||
- Hard-posterior K=3 membership is composition-sensitive across folds (max absolute deviation $12.8$ pp); K=3 is therefore not used to assign operational labels to CPAs.
|
||||
|
||||
The operational signature-level classifier of §III-L is calibrated against inter-CPA pair-level negative-anchor coincidence rates, not against mixture-derived antimodes. Cross-checks between the inherited five-way box rule and the K=3 partition appear in §III-K.
|
||||
The operational signature-level classifier of §III-H.1 is calibrated in §III-L against inter-CPA pair-level negative-anchor coincidence rates, not against mixture-derived antimodes. Cross-checks between the deployed five-way box rule and the K=3 partition appear in §III-K.
|
||||
|
||||
## K. Convergent Internal-Consistency Checks
|
||||
|
||||
The descriptive partition of §III-J is supported by three feature-derived per-CPA scores and a hard-ground-truth subset analysis. We caution at the outset that the three scores are **not statistically independent measurements** — all three are deterministic functions of the same per-CPA descriptor means $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ — so their high pairwise rank correlations are partly a mechanical consequence of shared inputs. Per §III-I.4, none of the three scores has a within-population bimodality interpretation; they are firm-compositional position scores at the accountant level. The checks below therefore document **internal consistency among feature-derived ranks**, not external validation against an independent hand-signed ground truth (which the corpus does not provide).
|
||||
The descriptive partition of §III-J is supported by three feature-derived per-CPA scores and a conservative hard-positive subset analysis. We caution at the outset that the three scores are **not statistically independent measurements** — all three are deterministic functions of the same per-CPA descriptor means $(\overline{\text{cos}}_a, \overline{\text{dHash}}_a)$ — so their high pairwise rank correlations are partly a mechanical consequence of shared inputs. Per §III-I.4, none of the three scores has a within-population bimodality interpretation; they are firm-compositional position scores at the accountant level. The checks below therefore document **internal consistency among feature-derived ranks**, not external validation against an independent hand-signed ground truth (which the corpus does not provide).
|
||||
|
||||
**1. Three feature-derived per-CPA scores (Script 38).** For each Big-4 CPA we compute:
|
||||
|
||||
- **Score 1 (K=3 posterior on the low-cos / high-dHash component):** $P(\text{C1})$ from the K=3 fit of §III-J. Per §III-J this is a firm-compositional position score on the (cos, dHash) plane (not a probability of any latent "hand-signing mechanism") — a function of both descriptor means.
|
||||
- **Score 2 (reverse-anchor cosine percentile):** the marginal cosine CDF value of $\overline{\text{cos}}_a$ under the non-Big-4 reference Gaussian of §III-H, sign-flipped so that lower percentile (further into the reference's left tail) corresponds to a Big-4 CPA whose mean cosine sits further from the templated end. This is a function of $\overline{\text{cos}}_a$ alone.
|
||||
- **Score 3 (inherited binary high-confidence box rule rate):** the per-CPA fraction of signatures that do **not** satisfy the inherited binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$). This is a per-signature-aggregated function of the same descriptors.
|
||||
- **Score 2 (reverse-anchor cosine percentile):** the marginal cosine CDF value of $\overline{\text{cos}}_a$ under the non-Big-4 reference Gaussian of §III-H.2, sign-flipped so that lower percentile (further into the reference's left tail) corresponds to a Big-4 CPA whose mean cosine sits further from the templated end. This is a function of $\overline{\text{cos}}_a$ alone.
|
||||
- **Score 3 (deployed binary high-confidence box rule rate):** the per-CPA fraction of signatures that do **not** satisfy the deployed binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$). This is a per-signature-aggregated function of the same descriptors.
|
||||
|
||||
Pairwise Spearman rank correlations among the three scores, $n = 437$ Big-4 CPAs (Script 38):
|
||||
|
||||
@@ -248,63 +249,53 @@ Pairwise Spearman rank correlations among the three scores, $n = 437$ Big-4 CPAs
|
||||
| Score 2 vs Score 3 | $+0.8890$ | $< 10^{-149}$ |
|
||||
| Score 1 vs Score 2 | $+0.8794$ | $< 10^{-142}$ |
|
||||
|
||||
We read this as the strongest internal-consistency signal in v4.0: three different summarisations of the same descriptor pair agree on the per-CPA descriptor-position ranking with $\rho > 0.87$. The three scores agree on placing Firm A as the most replication-dominated descriptor position and the three non-Firm-A Big-4 firms further from the templated end, but they do not all rank the non-Firm-A firms identically: the K=3 posterior P(C1) and the box-rule less-replication-dominated rate (Scores 1 and 3) place Firm C at the less-replication-dominated end of Big-4 (mean P(C1) $= 0.311$; mean box-rule less-replication-dominated rate $= 0.790$), while the reverse-anchor cosine percentile (Score 2) places Firm D fractionally higher than Firm C (mean reverse-anchor score $-0.7125$ vs Firm C $-0.7672$, with higher value indicating deeper into the reference left tail). The mean values for Firms B and D sit between Firms A and C on Scores 1 and 3 (Script 38 per-firm summary). We do not claim this constitutes external validation of any operational classifier; the inherited box rule is calibrated separately (§III-L), and the convergence above shows that a mixture-derived score and a reverse-anchor score concur with the box rule's per-CPA-aggregated outputs on the directional ordering, with a modest disagreement at the less-replication-dominated end between the three non-A Big-4 firms.
|
||||
We read this as the strongest internal-consistency signal in the analysis: three different summarisations of the same descriptor pair agree on the per-CPA descriptor-position ranking with $\rho > 0.87$. The three scores agree on placing Firm A as the most replication-dominated descriptor position and the three non-Firm-A Big-4 firms further from the templated end, but they do not all rank the non-Firm-A firms identically: the K=3 posterior P(C1) and the box-rule less-replication-dominated rate (Scores 1 and 3) place Firm C at the less-replication-dominated end of Big-4 (mean P(C1) $= 0.311$; mean box-rule less-replication-dominated rate $= 0.790$), while the reverse-anchor cosine percentile (Score 2) places Firm D fractionally higher than Firm C (mean reverse-anchor score $-0.7125$ vs Firm C $-0.7672$, with higher value indicating deeper into the reference left tail). The mean values for Firms B and D sit between Firms A and C on Scores 1 and 3 (Script 38 per-firm summary). We do not claim this constitutes external validation of any operational classifier; the deployed box rule is calibrated separately (§III-L), and the convergence above shows that a mixture-derived score and a reverse-anchor score concur with the box rule's per-CPA-aggregated outputs on the directional ordering, with a modest disagreement at the less-replication-dominated end between the three non-A Big-4 firms.
|
||||
|
||||
**2. Per-signature consistency (Script 39).** Per-CPA aggregation could in principle reflect averaging across within-CPA heterogeneity rather than coherent within-CPA behaviour. We test this by repeating the K=3 fit at the signature level — fitting a fresh K=3 GMM to the 150,442 Big-4 signature-level $(\text{cos}, \text{dHash}_{\text{indep}})$ points (Script 39) — and comparing labels. The per-CPA and per-signature K=3 fits recover a broadly similar three-component ordering; per-CPA C1 is at $\overline{\text{cos}} = 0.946$, $\overline{\text{dHash}} = 9.17$ vs per-signature C1 at $\overline{\text{cos}} = 0.928$, $\overline{\text{dHash}} = 9.75$ (an absolute cosine drift of $0.018$). Cohen $\kappa$ on the binary collapse (replication-dominated vs less-replication-dominated):
|
||||
|
||||
| Pair | Cohen $\kappa$ |
|
||||
|---|---|
|
||||
| Paper A binary high-confidence box rule vs per-CPA K=3 hard label | $0.662$ |
|
||||
| Paper A binary high-confidence box rule vs per-signature K=3 hard label | $0.559$ |
|
||||
| Deployed binary high-confidence box rule vs per-CPA K=3 hard label | $0.662$ |
|
||||
| Deployed binary high-confidence box rule vs per-signature K=3 hard label | $0.559$ |
|
||||
| Per-CPA K=3 vs per-signature K=3 | $0.870$ |
|
||||
|
||||
The Script 39 report verdict is `SIG_CONVERGENCE_MODERATE`. The $\kappa = 0.870$ between per-CPA-fit and per-signature-fit K=3 binary labels indicates that per-CPA aggregation does not collapse the broad three-component ordering. The lower $\kappa = 0.56\text{–}0.66$ between the binary box rule and either K=3 fit is consistent with two factors: different decision geometries (rectangular box vs Gaussian-mixture posterior boundary), and the fact that the binary box rule is a strict subset of the inherited five-way rule. We note that this comparison validates only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$); §III-K does not directly validate the five-way rule's `5 < \text{dHash} \leq 15` moderate-confidence band, which retains its v3.20.0 calibration and capture-rate evaluation (v3.20.0 Tables IX, XI, XII, XII-B; documented as inherited in §IV-J).
|
||||
The $\kappa = 0.870$ between per-CPA-fit and per-signature-fit K=3 binary labels indicates that per-CPA aggregation does not collapse the broad three-component ordering. The lower $\kappa = 0.56\text{–}0.66$ between the binary box rule and either K=3 fit is consistent with two factors: different decision geometries (rectangular box vs Gaussian-mixture posterior boundary), and the fact that the binary box rule is a strict subset of the five-way rule. This comparison checks only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$); §III-K does not directly check the five-way rule's `5 < \text{dHash} \leq 15` moderate-confidence band, whose calibration and capture-rate evidence is reported in the supplementary materials and not regenerated on the Big-4 subset.
|
||||
|
||||
**3. Leave-one-firm-out reproducibility (Scripts 36, 37).** Discussed in §III-J above. We summarise the joint result for cross-reference:
|
||||
|
||||
- *K=2 LOOO is unstable.* The maximum absolute deviation of the four fold cosine crossings from their across-fold mean is $0.028$, against the report's $0.005$ across-fold stability tolerance (Script 36; pairwise fold range $0.0376$, from $0.9380$ to $0.9756$). When Firm A is held out, the fold rule classifies $171/171$ of held-out Firm A CPAs as templated; when any non-Firm-A Big-4 firm is held out, the fold rule classifies $0$ of the held-out firm's CPAs as templated. This pattern indicates the K=2 boundary is essentially a Firm-A-vs-others separator rather than a within-Big-4 mechanism boundary.
|
||||
|
||||
- *K=3 LOOO is partially stable.* The C1 (low-cos / high-dHash) component shape is reproducible across folds: max deviation from the full-Big-4 baseline is $0.005$ in cosine, $0.96$ in dHash, and $0.023$ in mixture weight (Script 37). Hard-posterior membership remains composition-sensitive — observed absolute differences are $1.8$–$12.8$ pp across the four folds, with the Firm C fold exceeding the report's $5$ pp viability bar; the report's own verdict is `P2_PARTIAL` ("K=3 is not predictively useful as an operational classifier"). We accordingly do not use K=3 hard-posterior membership as an operational label.
|
||||
- *K=3 LOOO is partially stable.* The C1 (low-cos / high-dHash) component shape is reproducible across folds: max deviation from the full-Big-4 baseline is $0.005$ in cosine, $0.96$ in dHash, and $0.023$ in mixture weight (Script 37). Hard-posterior membership remains composition-sensitive — observed absolute differences are $1.8$–$12.8$ pp across the four folds, with the Firm C fold exceeding the report's $5$ pp viability bar; the report's own screening label is `P2_PARTIAL` ("K=3 is not predictively useful as an operational classifier"). We accordingly do not use K=3 hard-posterior membership as an operational label.
|
||||
|
||||
**4. Positive-anchor miss rate on byte-identical signatures (Script 40).** The corpus provides one hard ground-truth subset: signatures whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce pixel-identical images, so byte-identical signatures are conservative-subset ground truth for the *replicated* class. The Big-4 byte-identical subset comprises $n = 262$ signatures ($145 / 8 / 107 / 2$ across Firms A through D; Script 40).
|
||||
**4. Positive-anchor miss rate on byte-identical signatures (Script 40).** The corpus provides one conservative hard-positive subset: signatures whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce pixel-identical images, so byte-identical signatures are a conservative hard-positive subset for image replication. The Big-4 byte-identical subset comprises $n = 262$ signatures ($145 / 8 / 107 / 2$ across Firms A through D; Script 40).
|
||||
|
||||
We report each candidate check's *positive-anchor miss rate* — the fraction of byte-identical signatures classified as belonging to the less-replication-dominated descriptor positions. This is a one-sided check against a conservative positive subset, **not a paired specificity metric in the usual two-class sense**; we do not report a paired negative-anchor metric here because no signature-level hand-signed ground truth exists. The corresponding signature-level inter-CPA negative-anchor ICCR evidence is developed in §III-L.1 (Big-4 v4 sample) and the inherited corpus-wide v3.x version cited at §IV-I (reported under prior "FAR" terminology):
|
||||
We report each candidate check's *positive-anchor miss rate* — the fraction of byte-identical signatures classified as belonging to the less-replication-dominated descriptor positions. This is a one-sided check against a conservative positive subset, **not a paired specificity metric in the usual two-class sense**; we do not report a paired negative-anchor metric here because no signature-level hand-signed ground truth exists. The corresponding signature-level inter-CPA negative-anchor ICCR evidence is developed in §III-L.1 (Big-4 sample) and the corpus-wide version cited at §IV-I:
|
||||
|
||||
| Candidate check | Pixel-identity miss rate (Wilson 95% CI) |
|
||||
|---|---|
|
||||
| Inherited Paper A binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) | $0\%$ $[0\%, 1.45\%]$ |
|
||||
| Deployed binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) | $0\%$ $[0\%, 1.45\%]$ |
|
||||
| K=3 per-CPA hard label (C3 high-cos / low-dHash corner; descriptive only) | $0\%$ $[0\%, 1.45\%]$ |
|
||||
| Reverse-anchor with prevalence-calibrated cut | $0\%$ $[0\%, 1.45\%]$ |
|
||||
|
||||
All three candidate scores correctly assign every byte-identical signature to the replicated class. We caution that for the inherited box rule this result is close to tautological: byte-identical nearest-neighbour signatures have cosine $\approx 1$ and dHash $\approx 0$ by construction, so any threshold strictly below cos $= 1$ and strictly above dHash $= 0$ will capture them; v3.x discussed this conservative-subset caveat at length (v3 §III-J item 1, V-F). The positive-anchor miss rate is therefore a necessary check (a classifier that *failed* this check would be disqualified), not a sufficient validation of the classifier's behaviour on the non-byte-identical replicated population. The reverse-anchor cut here is chosen by prevalence calibration against the inherited box rule's overall replicated rate ($49.58\%$ of Big-4 signatures; Script 40); this is a documented v4.0 limitation since no signature-level hand-signed ground truth exists to permit direct ROC optimisation.
|
||||
All three candidate scores correctly assign every byte-identical signature to the replicated class. We caution that for the box rule this result is close to tautological: byte-identical nearest-neighbour signatures have cosine $\approx 1$ and dHash $\approx 0$ by construction, so any threshold strictly below cos $= 1$ and strictly above dHash $= 0$ will capture them. The positive-anchor miss rate is therefore a necessary check (a classifier that *failed* this check would be disqualified), not a sufficient validation of the classifier's behaviour on the non-byte-identical replicated population. The reverse-anchor cut here is chosen by prevalence calibration against the box rule's overall replicated rate ($49.58\%$ of Big-4 signatures); this is a documented limitation since no signature-level hand-signed ground truth exists to permit direct ROC optimisation.
|
||||
|
||||
## L. Anchor-Based Threshold Calibration and Operational Classifier
|
||||
## L. Anchor-Based Threshold Calibration
|
||||
|
||||
§III-I.4 established that the descriptor distributions do not contain a within-population bimodal antimode that could anchor an operational threshold; the K=3 mixture of §III-J is a descriptive firm-compositional partition, not a mechanism-cluster model. This section develops v4.0's anchor-based threshold calibration: the operational thresholds inherited from v3.x are characterised by their inter-CPA pair-level negative-anchor coincidence behaviour and their pool-normalised per-signature and per-document alert behaviour, at multiple units of analysis. Throughout this section we report **inter-CPA coincidence rates** rather than "False Acceptance Rates"; we explain the terminological choice in §III-L.0.
|
||||
The operational classifier defined in §III-H.1 is calibrated by characterising the deployed thresholds' inter-CPA pair-level negative-anchor coincidence behaviour and their pool-normalised per-signature and per-document alert behaviour, at multiple units of analysis. §III-I.4 establishes that the descriptor distributions do not contain a within-population bimodal antimode that could anchor an operational threshold; the K=3 mixture of §III-J is a descriptive firm-compositional partition, not a mechanism-cluster model. Throughout this section we report **inter-CPA coincidence rates** rather than "False Acceptance Rates"; we explain the terminological choice in §III-L.0.
|
||||
|
||||
### L.0. Calibration methodology
|
||||
|
||||
**Operational classifier (inherited from v3.20.0 §III-K, retained unchanged).** Each Big-4 signature is assigned to one of five categories using the per-signature descriptor pair $(\text{cos}_s, \text{dHash}_s)$ where $\text{cos}_s$ is the maximum cosine similarity to another signature by the same CPA and $\text{dHash}_s$ is the minimum independent dHash to another signature by the same CPA:
|
||||
|
||||
1. **High-confidence non-hand-signed (HC):** Cosine $> 0.95$ AND $\text{dHash}_{\text{indep}} \leq 5$. Both descriptors converge on strong replication evidence.
|
||||
2. **Moderate-confidence non-hand-signed (MC):** Cosine $> 0.95$ AND $5 < \text{dHash}_{\text{indep}} \leq 15$. Feature-level evidence is strong; structural similarity is present but below the high-confidence cutoff.
|
||||
3. **High style consistency (HSC):** Cosine $> 0.95$ AND $\text{dHash}_{\text{indep}} > 15$. High feature-level similarity without structural corroboration — consistent with a CPA who signs very consistently but not via image reproduction.
|
||||
4. **Uncertain (UN):** Cosine between the all-pairs intra/inter KDE crossover ($0.837$) and $0.95$.
|
||||
5. **Likely hand-signed (LH):** Cosine $\leq 0.837$.
|
||||
|
||||
The thresholds ($\text{cos} = 0.95$ as the cosine operating point, $\text{cos} = 0.837$ as the all-pairs KDE crossover, $\text{dHash} = 5$ and $15$ as structural-similarity sub-band cutoffs) are inherited from v3.x §III-K and retain their v3.x calibration provenance. Document-level labels are aggregated via the v3.x worst-case rule: each audit report inherits the most-replication-consistent category among its certifying-CPA signatures (rank order HC > MC > HSC > UN > LH).
|
||||
|
||||
**Why retained without v4.0 recalibration.** The inherited thresholds preserve continuity with v3.x reporting and with the existing literature. §III-I.4 establishes that a v4.0 recalibration cannot be anchored on distributional antimodes (no within-population bimodality exists); §III-L.1 confirms that the cosine threshold's specificity behaviour at the inter-CPA pair level (the v3.x calibration anchor) is reproducible on the v4 spike sample, and §III-L.1 newly characterises the structural-dimension threshold $\text{dHash} \leq 5$'s pair-level coincidence behaviour. Sub-band thresholds ($\text{dHash} = 15$, $\text{cos} = 0.837$) retain v3.x's inherited calibration; v4.0 does not provide independent calibration for those sub-bands.
|
||||
**Calibration role of the present analysis.** The deployed thresholds of §III-H.1 preserve continuity with the existing literature and the supplementary calibration evidence. §III-I.4 establishes that a recalibration cannot be anchored on distributional antimodes (no within-population bimodality exists); §III-L.1 below characterises the cosine threshold's specificity-proxy behaviour at the inter-CPA pair level and the structural-dimension threshold $\text{dHash} \leq 5$'s pair-level coincidence behaviour. The sub-band thresholds ($\text{dHash} = 15$, $\text{cos} = 0.837$) retain their supplementary calibration evidence; the present calibration does not provide independent rates for those sub-bands.
|
||||
|
||||
**Three units of analysis.** We report inter-CPA negative-anchor coincidence behaviour at three units, each addressing a different operational question:
|
||||
|
||||
- *Per comparison.* For a randomly drawn pair of signatures from different CPAs, what fraction satisfies the rule (cos $>$ cos\_threshold and / or dHash $\leq$ dHash\_threshold)? This is the unit at which v3.x §IV-I characterised the cosine threshold's specificity behaviour and at which threshold-derivation in biometric verification is conventionally calibrated. We report it for both the cosine and dHash dimensions, marginally and jointly (§III-L.1).
|
||||
- *Per comparison.* For a randomly drawn pair of signatures from different CPAs, what fraction satisfies the rule (cos $>$ cos\_threshold and / or dHash $\leq$ dHash\_threshold)? This is the conventional pairwise calibration unit in biometric verification. We report it for both the cosine and dHash dimensions, marginally and jointly (§III-L.1).
|
||||
- *Per signature pool.* For a Big-4 source signature $s$ with same-CPA pool of size $n_{\text{pool}}(s)$, what is the probability that the deployed rule fires *under the counterfactual* of replacing the source's same-CPA pool with $n_{\text{pool}}(s)$ random non-same-CPA candidates? This addresses the standard concern that a per-pair rate computed on independent pairs is not the deployed-rule rate at the per-signature classifier level: the deployed rule takes max-cosine and min-dHash over a pool of size $n_{\text{pool}}(s)$, so its effective coincidence rate is approximately $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ in the independence limit (§III-L.2).
|
||||
- *Per document.* For an audit report aggregated via the worst-case rule, what fraction of documents have at least one signature whose deployed pool-normalised rule fires under the same inter-CPA candidate-replacement counterfactual? This is the operational alarm-rate unit (§III-L.3).
|
||||
|
||||
**Any-pair vs same-pair semantics.** The deployed rule uses independent extrema: a signature satisfies the HC rule if $\max_{\text{pool}} \text{cos} > 0.95$ AND $\min_{\text{pool}} \text{dHash} \leq 5$, *not* if a single candidate in the pool satisfies both. We refer to this as the **any-pair** rule. A stricter alternative — the **same-pair** rule — requires a single candidate to satisfy both inequalities; the deployed v3/v4 rule is any-pair, but we report same-pair as a stricter alternative classifier where useful (§III-L.2, §III-L.4).
|
||||
**Any-pair vs same-pair semantics.** The deployed rule uses independent extrema: a signature satisfies the HC rule if $\max_{\text{pool}} \text{cos} > 0.95$ AND $\min_{\text{pool}} \text{dHash} \leq 5$, *not* if a single candidate in the pool satisfies both. We refer to this as the **any-pair** rule. A stricter alternative — the **same-pair** rule — requires a single candidate to satisfy both inequalities; the deployed rule is any-pair, but we report same-pair as a stricter alternative classifier where useful (§III-L.2, §III-L.4).
|
||||
|
||||
**Terminological note on "FAR".** The v3.x and biometric-verification literature speak of "False Acceptance Rate" (FAR) for a per-pair rate computed on independent inter-CPA pairs. We adopt **inter-CPA coincidence rate (ICCR)** as the v4.0 metric name and *do not* use "FAR" in the manuscript prose, for two reasons: (a) FAR has a specific biometric-verification meaning that requires ground-truth negative labels (which the corpus does not provide at the signature level); (b) §III-L.4 shows that the inter-CPA negative-anchor assumption — that inter-CPA pairs are negative — is partially violated by within-firm cross-CPA template-like collision structures. Reading "inter-CPA coincidence rate" as a *specificity proxy* under an explicitly disclosed assumption is faithful to the evidence; reading it as a true biometric FAR would overstate the evidence. We retain the v3.x numerical results (which are quantitatively reproduced in §III-L.1) under the new terminology.
|
||||
**Terminological note on "FAR".** The biometric-verification literature speaks of "False Acceptance Rate" (FAR) for a per-pair rate computed on independent inter-CPA pairs. We adopt **inter-CPA coincidence rate (ICCR)** as the metric name and *do not* use "FAR" in the manuscript prose, for two reasons: (a) FAR has a specific biometric-verification meaning that requires ground-truth negative labels (which the corpus does not provide at the signature level); (b) §III-L.4 shows that the inter-CPA negative-anchor assumption — that inter-CPA pairs are negative — is partially violated by within-firm cross-CPA template-like collision structures. Reading "inter-CPA coincidence rate" as a *specificity proxy* under an explicitly disclosed assumption is faithful to the evidence; reading it as a true biometric FAR would overstate the evidence.
|
||||
|
||||
### L.1. Per-comparison inter-CPA coincidence rate (Script 40b)
|
||||
|
||||
@@ -313,21 +304,21 @@ We sample $5 \times 10^5$ inter-CPA pairs uniformly at random from Big-4 signatu
|
||||
| Threshold | Per-comparison inter-CPA coincidence rate | 95% Wilson CI |
|
||||
|---|---|---|
|
||||
| Cosine $> 0.95$ | $0.00060$ | $[0.00053, 0.00067]$ |
|
||||
| Cosine $> 0.945$ (v3.x published "natural threshold") | $0.00081$ | $[0.00073, 0.00089]$ |
|
||||
| Cosine $> 0.945$ (alternative operating point from supplementary calibration evidence) | $0.00081$ | $[0.00073, 0.00089]$ |
|
||||
| Cosine $> 0.97$ | $0.00024$ | $[0.00020, 0.00029]$ |
|
||||
| Cosine $> 0.98$ | $0.00009$ | $[0.00007, 0.00012]$ |
|
||||
| dHash $\leq 5$ | $0.00129$ | $[0.00120, 0.00140]$ |
|
||||
| dHash $\leq 4$ | $0.00050$ | $[0.00044, 0.00057]$ |
|
||||
| dHash $\leq 3$ | $0.00019$ | $[0.00015, 0.00023]$ |
|
||||
| dHash $\leq 2$ | $0.00006$ | $[0.00004, 0.00008]$ |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 5$ | $0.00014$ | (any-pair semantics) |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 4$ | $0.00011$ | (any-pair semantics) |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 5$ (any-pair semantics) | $0.00014$ | $[0.00011, 0.00018]$ |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 4$ (any-pair) | $0.00011$ | $[0.00008, 0.00014]$ |
|
||||
|
||||
The cosine row at $\text{cos} > 0.95$ replicates the v3.x §IV-I Table X result (v3.x reported the per-comparison rate as $0.0005$ under prior "FAR" terminology from a similarly-sized inter-CPA negative anchor; the v4 spike on a $5 \times 10^5$-pair sample yields $0.00060$, within the v3.x reported precision). The dHash and joint rows are v4-new: v3.x calibration did not provide an inter-CPA pair-level coincidence rate for the structural dimension or the joint rule.
|
||||
The cosine row at $\text{cos} > 0.95$ is consistent with the corpus-wide per-comparison rate of $0.0005$ reported in §IV-I on a similarly-sized inter-CPA sample; the present $5 \times 10^5$-pair sample yields $0.00060$, within that precision. The dHash row and joint row are reported here for the first time; the corpus-wide spike did not provide an inter-CPA pair-level coincidence rate for the structural dimension or the joint rule.
|
||||
|
||||
The all-firms-scope sample yields slightly lower per-comparison coincidence rates (cos $> 0.95$: $0.00031$; dHash $\leq 5$: $0.00073$; joint: $0.00007$); the all-firms sample weights small CPAs more heavily under CPA-uniform pair sampling, so we treat the Big-4 sample as the primary calibration scope and report all-firms as a corroborating-scope robustness check.
|
||||
|
||||
**Conditional inter-CPA coincidence rate.** A natural follow-up question is whether the dHash dimension provides marginal specificity beyond the cosine gate. For pairs with cos $> 0.95$, the conditional rate of dHash $\leq 5$ is $0.234$ (Wilson 95% CI $[0.190, 0.285]$; $70$ of $299$ pairs in the Big-4 sample). At cos $> 0.95$, dHash provides $\sim 4.3\times$ further per-comparison specificity (joint $0.00014$ vs cos-only $0.00060$).
|
||||
**Conditional inter-CPA coincidence rate.** A natural follow-up question is whether the dHash dimension provides marginal specificity beyond the cosine gate. For pairs with cos $> 0.95$, the conditional rate of dHash $\leq 5$ is $0.234$ (Wilson 95% CI $[0.190, 0.285]$; $70$ of $299$ pairs in the Big-4 sample). At cos $> 0.95$, dHash provides $\sim 4.3\times$ further per-comparison ICCR refinement (joint $0.00014$ vs cos-only $0.00060$).
|
||||
|
||||
The per-comparison rate is a useful *specificity-proxy calibration* for the deployed rule's pair-level behaviour. It does *not* directly translate to the deployed-rule specificity at the per-signature classifier level, because the deployed classifier takes extrema over a same-CPA pool of size $n_{\text{pool}}$. The pool-normalised inter-CPA alert rate is reported in §III-L.2.
|
||||
|
||||
@@ -351,18 +342,18 @@ Per-firm any-pair rates (no bootstrap; descriptive):
|
||||
| Firm C | $38{,}616$ | $0.0053$ | $0.0019$ |
|
||||
| Firm D | $17{,}133$ | $0.0110$ | $0.0051$ |
|
||||
|
||||
**Pool-size decile dependence.** The deployed rule's pool-normalised rate is monotonically (broadly) increasing in $n_{\text{pool}}$, consistent with the $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ form expected under inter-CPA independence (Script 43 decile table). Decile 1 (smallest pools, $n_{\text{pool}} \in [0, 201]$): any-pair ICCR $= 0.0249$. Decile 10 (largest, $n_{\text{pool}} \in [846, 1115]$): any-pair ICCR $= 0.1905$. The trend is broadly monotonic with two minor non-monotone reversals (decile 5 and decile 9 dip below their predecessors).
|
||||
**Pool-size decile dependence.** The deployed rule's pool-normalised rate is monotonically (broadly) increasing in $n_{\text{pool}}$, consistent with the $1 - (1 - p_{\text{pair}})^{n_{\text{pool}}}$ form expected under inter-CPA independence (Script 43 decile table). This functional form is used as descriptive intuition for the broad monotone trend, not as an independence assumption used for estimation; the within-firm violation of inter-CPA independence (§III-L.4) bounds how literally the closed form can be read. Decile 1 (smallest pools, $n_{\text{pool}} \in [0, 201]$): any-pair ICCR $= 0.0249$. Decile 10 (largest, $n_{\text{pool}} \in [846, 1115]$): any-pair ICCR $= 0.1905$. The trend is broadly monotonic with two minor non-monotone reversals (decile 5 and decile 9 dip below their predecessors).
|
||||
|
||||
**Threshold sensitivity at per-signature unit.** Tightening the HC rule from $\text{dHash} \leq 5$ to $\text{dHash} \leq 3$ (same-pair) reduces the per-signature ICCR from $0.0827$ to $0.0449$ (Big-4 pooled); tightening to $\text{dHash} \leq 4$ gives $0.0639$ (same-pair). Stakeholders requiring a per-signature ICCR of $\leq 0.05$ at HC can adopt dHash $\leq 3$ same-pair as a stricter operating point; at $\leq 0.10$ the inherited HC any-pair rule with $\text{dHash} \leq 5$ at $0.1102$ is within tolerance.
|
||||
**Threshold sensitivity at per-signature unit.** Tightening the HC rule from $\text{dHash} \leq 5$ to $\text{dHash} \leq 3$ (same-pair) reduces the per-signature ICCR from $0.0827$ to $0.0449$ (Big-4 pooled); tightening to $\text{dHash} \leq 4$ gives $0.0639$ (same-pair). A stricter operating point of dHash $\leq 3$ same-pair would correspond to a per-signature ICCR of $\approx 0.05$; the deployed HC any-pair rule with $\text{dHash} \leq 5$ corresponds to $\approx 0.11$. Stakeholders requiring a tighter specificity proxy could consider the dHash $\leq 3$ same-pair variant, with the unsupervised-setting caveats of §III-M.
|
||||
|
||||
### L.3. Document-level inter-CPA proxy alert rate (Script 45)
|
||||
|
||||
The deployed worst-case aggregation classifies each document by the most-replication-consistent category among its constituent signatures (§III-L.0). Three operationally meaningful document-level alarm definitions are reported, each as the fraction of documents whose worst-case signature category falls in the alarm set under the same inter-CPA candidate-pool counterfactual as §III-L.2 (Script 45; $n_{\text{docs}} = 75{,}233$ Big-4 documents):
|
||||
The deployed worst-case aggregation classifies each document by the most-replication-consistent category among its constituent signatures (§III-H.1). Three operationally meaningful document-level alarm definitions are reported, each as the fraction of documents whose worst-case signature category falls in the alarm set under the same inter-CPA candidate-pool counterfactual as §III-L.2 (Script 45; $n_{\text{docs}} = 75{,}233$ Big-4 documents):
|
||||
|
||||
| Alarm definition | Alarm set | Document-level ICCR | Wilson 95% CI |
|
||||
|---|---|---|---|
|
||||
| D1 | HC only | $0.1797$ | $[0.1770, 0.1825]$ |
|
||||
| D2 | HC + MC ("any non-hand-signed verdict") | $0.3375$ | $[0.3342, 0.3409]$ |
|
||||
| D2 | HC + MC ("any non-hand-signed screening label") | $0.3375$ | $[0.3342, 0.3409]$ |
|
||||
| D3 | HC + MC + HSC | $0.3384$ | $[0.3351, 0.3418]$ |
|
||||
|
||||
Per-firm D2 document-level rates:
|
||||
@@ -387,7 +378,7 @@ The document-level D2 rate of $33.75\%$ pooled over Big-4 is the most operationa
|
||||
| Firm D | $0.027$ | $< 1$ | $\sim 37\times$ lower odds than Firm A |
|
||||
| log(pool size, centred) | $4.01$ | $> 1$ | $\sim 4\times$ higher odds per unit log pool size |
|
||||
|
||||
The Firm B/C/D odds ratios are very small after controlling for pool size, indicating that firm membership accounts for a large multiplicative effect on the per-signature rate that is *not* explained by pool size alone. (We report odds ratios rather than $z$-scores because per-signature observations are clustered by CPA and firm, and naive standard errors would be inflated by within-cluster correlation; a cluster-robust standard error analysis is left as a robustness check.)
|
||||
The Firm B/C/D odds ratios are very small after controlling for pool size, indicating that firm membership accounts for a large multiplicative effect on the per-signature rate that is *not* explained by pool size alone. (We report odds ratios rather than $z$-scores because per-signature observations are clustered by CPA and firm, and naive standard errors would be unreliable under within-cluster correlation; a cluster-robust standard error analysis is left as a robustness check.)
|
||||
|
||||
The per-decile per-firm breakdown (Script 44) confirms the pattern: within every pool-size decile, Firms B/C/D have rates of $0.0006$–$0.0358$, while Firm A's rate ranges $0.0541$–$0.5958$ across deciles. The firm gap is large within matched pool sizes, not driven by pool composition.
|
||||
|
||||
@@ -402,61 +393,40 @@ The per-decile per-firm breakdown (Script 44) confirms the pattern: within every
|
||||
|
||||
For the same-pair joint event (a single candidate satisfying both $\text{cos} > 0.95$ and $\text{dHash} \leq 5$), the candidate firm is even more strongly concentrated within the source firm: Firm A source $\to$ Firm A candidate in $11{,}314$ of $11{,}319$ same-pair hits ($99.96\%$); Firm B source $\to$ Firm B candidate in $85$ of $87$ ($97.7\%$); Firm C source $\to$ Firm C candidate in $54$ of $55$ ($98.2\%$); Firm D source $\to$ Firm D candidate in $64$ of $66$ ($97.0\%$).
|
||||
|
||||
**Interpretation.** Under the deployed any-pair rule, the within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D — Firm A's pattern is markedly more within-firm-concentrated than the other three firms', though every Big-4 firm still has more than three quarters of its any-pair collisions falling on candidates within the same firm. The stricter same-pair joint event — a single candidate satisfying both cos $> 0.95$ and dHash $\leq 5$ — saturates at $97.0$–$99.96\%$ within-firm across all four firms. This pattern is consistent with — but not by itself diagnostic of — firm-specific template, stamp, or document-production reuse: within-firm scanning workflows, common form templates, and shared report-generation infrastructure could produce visually similar signature crops across different CPAs within the same firm. The byte-level evidence of v3.x §IV-F.1 (Firm A's $145$ pixel-identical signatures across $\sim 50$ distinct certifying partners) provides direct evidence that firm-level template reuse does occur at Firm A; the broader inter-CPA collision pattern in §III-L.4 is consistent with that mechanism extending in milder form to Firms B/C/D. We report this as "inter-CPA collision concentration is within-firm" — a descriptive observation about deployed-rule behaviour — and refrain from inferring that the within-firm hits constitute deliberate or systematic template sharing.
|
||||
**Interpretation.** Under the deployed any-pair rule, the within-firm collision concentration is $98.8\%$ at Firm A and $76.7$–$83.7\%$ at Firms B/C/D — Firm A's pattern is markedly more within-firm-concentrated than the other three firms', though every Big-4 firm still has more than three quarters of its any-pair collisions falling on candidates within the same firm. The stricter same-pair joint event — a single candidate satisfying both cos $> 0.95$ and dHash $\leq 5$ — saturates at $97.0$–$99.96\%$ within-firm across all four firms. This pattern is consistent with — but not by itself diagnostic of — firm-specific template, stamp, or document-production reuse: within-firm scanning workflows, common form templates, and shared report-generation infrastructure could produce visually similar signature crops across different CPAs within the same firm. Byte-level decomposition of Firm A's $145$ pixel-identical signatures across $\sim 50$ distinct certifying partners (supplementary materials; §III-H.2) provides direct evidence of image-level reuse among Firm A signatures; the distribution across many partners is consistent with a firm-level template or production workflow, and the broader inter-CPA collision pattern in §III-L.4 is consistent with similar, milder within-firm collision patterns at Firms B/C/D, whose mechanisms may include template-like reuse, digitisation-pipeline homogeneity, or signing-style homogeneity (§V-H). We report this as "inter-CPA collision concentration is within-firm" — a descriptive observation about deployed-rule behaviour — and refrain from inferring that the within-firm hits constitute deliberate or systematic template sharing.
|
||||
|
||||
This connects back to §III-J: the K=3 firm-composition contrast at the accountant level (Firm A dominating C3; Firm C dominating C1) reappears at the deployment level in the cross-firm hit matrix, where the within-firm collision concentration is the dominant pattern at all four Big-4 firms — most strongly at Firm A ($98.8\%$ any-pair, $99.96\%$ same-pair) and at materially lower but still majority levels at Firms B/C/D ($76.7$–$83.7\%$ any-pair; $97.0$–$98.2\%$ same-pair).
|
||||
|
||||
### L.5. Alert-rate sensitivity around inherited thresholds (Script 46)
|
||||
### L.5. Alert-rate sensitivity around deployed thresholds (Script 46)
|
||||
|
||||
To test whether the inherited cosine threshold $0.95$ and dHash threshold $5$ coincide with a low-gradient (plateau-stable) region of the deployed-rule alert-rate surface — which would be weak distributional evidence that the inherited thresholds are stable operating points — we sweep each threshold across a range and report the per-signature alert rate on actual observed Big-4 same-CPA pools (not inter-CPA-replaced pools), comparing the local gradient at the inherited threshold to the median gradient across the sweep (Script 46).
|
||||
To test whether the deployed cosine threshold $0.95$ and dHash threshold $5$ coincide with a low-gradient (plateau-stable) region of the deployed-rule alert-rate surface — which would be weak distributional evidence that the deployed thresholds are stable operating points — we sweep each threshold across a range and report the per-signature alert rate on actual observed Big-4 same-CPA pools (not inter-CPA-replaced pools), comparing the local gradient at the deployed threshold to the median gradient across the sweep (Script 46).
|
||||
|
||||
At the inherited HC operating point cos $> 0.95$ AND dHash $\leq 5$, the local gradient of the per-signature alert rate is substantially larger than the median gradient across the sweep (cosine: ratio $\approx 25\times$ at the $0.95$ point relative to median; dHash: ratio $\approx 3.8\times$ at the $5$ point relative to median; both Script 46). Reading these ratios descriptively, the inherited HC threshold is *locally sensitive* rather than plateau-stable: small threshold perturbations materially change the deployed alert rate (cosine sweep at dHash $\leq 5$ yields rates of $0.5091$ at cos $> 0.945$ vs $0.4789$ at cos $> 0.955$, a $3.0$ pp swing across a $0.01$ cosine perturbation; dHash sweep at cos $> 0.95$ yields rates of $0.4207$ at dHash $\leq 4$ vs $0.5639$ at dHash $\leq 6$, a $14.3$ pp swing across a single integer step). The local-gradient-to-median-gradient ratios are descriptive diagnostics, not formal plateau tests; the primary evidence for "no within-population bimodal antimode at these thresholds" comes from §III-I.4's composition decomposition, not from §III-L.5.
|
||||
At the deployed HC operating point cos $> 0.95$ AND dHash $\leq 5$, the local gradient of the per-signature alert rate is substantially larger than the median gradient across the sweep (cosine: ratio $\approx 25\times$ at the $0.95$ point relative to median; dHash: ratio $\approx 3.8\times$ at the $5$ point relative to median; both Script 46). Reading these ratios descriptively, the deployed HC threshold is *locally sensitive* rather than plateau-stable: small threshold perturbations materially change the deployed alert rate (cosine sweep at dHash $\leq 5$ yields rates of $0.5091$ at cos $> 0.945$ vs $0.4789$ at cos $> 0.955$, a $3.0$ pp swing across a $0.01$ cosine perturbation; dHash sweep at cos $> 0.95$ yields rates of $0.4207$ at dHash $\leq 4$ vs $0.5639$ at dHash $\leq 6$, a $14.3$ pp swing across a single integer step). The local-gradient-to-median-gradient ratios are descriptive diagnostics, not formal plateau tests; the primary evidence for "no within-population bimodal antimode at these thresholds" comes from §III-I.4's composition decomposition, not from §III-L.5.
|
||||
|
||||
The MC/HSC boundary at dHash $= 15$, by contrast, *is* in a low-gradient region (ratio $\approx 0.08$ to the median); the plateau-like behaviour around dHash $= 15$ is corroborating evidence that the high-end structural threshold lies in a regime where the rule's alert rate is approximately saturated, consistent with the high-dHash tail behaviour expected once near-identical pairs have been exhausted. The §III-L.5 non-plateau / local-sensitivity finding therefore applies specifically to the HC cutoff (cos $= 0.95$, dHash $= 5$); the MC/HSC sub-band boundary at dHash $= 15$ exhibits the opposite behaviour and is plateau-like.
|
||||
|
||||
We interpret the inherited HC thresholds as **specificity-anchored operating points** chosen for the specificity-vs-alert-yield tradeoff (§III-L.1), *not* as distributional antimodes. Stakeholders requiring different operating points on the tradeoff curve can derive thresholds by inverting the per-comparison or pool-normalised ICCR curves (§III-L.1, §III-L.2) at their preferred specificity target.
|
||||
We interpret the deployed HC thresholds as **specificity-anchored operating points** chosen for the specificity-vs-alert-yield tradeoff (§III-L.1), *not* as distributional antimodes. Alternative operating points on the tradeoff curve can be characterised by inverting the per-comparison or pool-normalised ICCR curves (§III-L.1, §III-L.2) at the preferred specificity target.
|
||||
|
||||
### L.6. Observed deployed alert rate on actual same-CPA pools
|
||||
|
||||
The pool-normalised inter-CPA rates of §III-L.2 and §III-L.3 use the counterfactual of replacing the source signature's same-CPA pool with random non-same-CPA candidates. The **observed deployed alert rate** uses the source's actual same-CPA pool, i.e., the rate at which the deployed rule fires on the real corpus. For Big-4, the inherited HC any-pair rule fires on $49.58\%$ of signatures and $62.28\%$ of documents (Script 46; Script 42 reproduces the per-signature rate at $49.58\%$).
|
||||
The pool-normalised inter-CPA rates of §III-L.2 and §III-L.3 use the counterfactual of replacing the source signature's same-CPA pool with random non-same-CPA candidates. The **observed deployed alert rate** uses the source's actual same-CPA pool, i.e., the rate at which the deployed rule fires on the real corpus. For Big-4, the deployed HC any-pair rule fires on $49.58\%$ of signatures and $62.28\%$ of documents (Script 46; Script 42 reproduces the per-signature rate at $49.58\%$).
|
||||
|
||||
The per-signature observed-deployed rate is $\sim 4.5\times$ the pool-normalised inter-CPA rate ($0.4958$ vs $0.1102$); the per-document observed-deployed rate is $\sim 3.5\times$ the pool-normalised inter-CPA D1 (HC) rate ($0.6228$ vs $0.1797$). We refer to this multiplicative gap as the **deployed-rate excess over the inter-CPA proxy**:
|
||||
|
||||
- Per-signature: $0.4958 - 0.1102 = 0.3856$ ($38.6$ pp excess)
|
||||
- Per-document HC: $0.6228 - 0.1797 = 0.4431$ ($44.3$ pp excess)
|
||||
|
||||
We *do not* interpret the deployed-rate excess as a presumed true-positive rate; the inferential limits of this interpretation are developed in §III-M. The deployed-rate excess is best read as a *same-CPA repeatability signal* — a quantity that exceeds what random inter-CPA candidate replacement would produce — rather than as an estimate of true replication prevalence.
|
||||
We *do not* interpret the deployed-rate excess as a presumed true-positive rate; the inferential limits of this interpretation are developed in §III-M. The deployed-rate excess is best read as an *observed same-CPA-pool excess* — a quantity that exceeds what random inter-CPA candidate replacement would produce — whose mechanism is not identifiable from descriptor-only data (§III-M); we do not attribute it to within-CPA handwriting repeatability or to image replication without further evidence.
|
||||
|
||||
### L.7. K=3 not used as classifier
|
||||
## M. Unsupervised Diagnostic Strategy and Limits
|
||||
|
||||
The K=3 mixture of §III-J is reported in §IV as an accountant-level descriptive summary alongside the per-signature five-way classifier. We do not assign signature-level or document-level labels from the K=3 mixture in any v4.0 result table; the K=3 hard label is used only for the accountant-level firm × cluster cross-tabulation (§III-J; Script 35), and the K=3 *posterior* P(C1) is used (as the continuous Score 1) in the internal-consistency Spearman correlations of §III-K. The operational classifier of §III-L.0 is the inherited v3.x five-way box rule; the calibration evidence in §III-L.1 through §III-L.6 characterises its multi-level coincidence behaviour against the inter-CPA negative anchor.
|
||||
The corpus lacks signature-level ground-truth replication labels: no signature is annotated as definitively hand-signed or definitively templated. The conservative positive anchor (pixel-identical same-CPA signatures; §III-K.4) is by construction near $\text{cos} = 1$ and $\text{dHash} = 0$, providing a tautological capture-check rather than a sensitivity estimate for the non-byte-identical replicated class. The corpus therefore does not admit standard supervised classifier validation: we cannot report False Rejection Rate, sensitivity, recall, Equal Error Rate, ROC-AUC, or precision against ground truth.
|
||||
|
||||
## M. Validation Strategy and Limitations under Unsupervised Setting
|
||||
Each diagnostic reported in this paper therefore addresses one specific failure mode of an unsupervised screening classifier; the full diagnostic-to-failure-mode-to-assumption map is given in Appendix A Table A.II. No single diagnostic provides ground-truth validation; together they define the limits of what can be supported in this corpus without signature-level ground truth.
|
||||
|
||||
The v4.0 corpus lacks signature-level ground-truth replication labels: no signature is annotated as definitively hand-signed or definitively templated. The conservative positive anchor (pixel-identical same-CPA signatures; §III-K.4 and v3.x §IV-F.1) is by construction near $\text{cos} = 1$ and $\text{dHash} = 0$, providing a tautological capture-check rather than a sensitivity estimate for the non-byte-identical replicated class. The corpus therefore does not admit standard supervised classifier validation: we cannot report False Rejection Rate, sensitivity, recall, Equal Error Rate, ROC-AUC, or precision against ground truth.
|
||||
**Limits of the present analysis.** We do not claim a validated forensic detector or an autonomous classification system. We do not report False Rejection Rate, sensitivity, recall, EER, ROC-AUC, precision, or positive predictive value against ground truth, because no ground truth exists at the signature level. We do not interpret the deployed-rate excess of §III-L.6 as a presumed true-positive rate: that interpretation would require assuming that the within-firm same-CPA pool's collision rate equals the inter-CPA proxy rate in the absence of replication (i.e., that genuine same-CPA hand-signing would produce a collision rate no higher than random inter-CPA pairs). Two factors make the assumption unsafe: (a) a CPA who signs consistently can produce stylistically similar signatures across years that exceed inter-CPA similarity at the cosine axis; (b) within-firm template sharing (§III-L.4 cross-firm hit matrix; byte-level evidence of Firm A's pixel-identical signatures across partners, supplementary materials) places a substantial inter-CPA collision floor that itself reflects template-like reuse rather than independent inter-CPA random matching. We do not infer that the within-firm collision concentration of §III-L.4 constitutes deliberate template sharing; we describe it as "inter-CPA collision concentration is within-firm" and treat the mechanism as an open empirical question.
|
||||
|
||||
In place of supervised validation, v4.0 adopts a **multi-tool collection of partial-evidence diagnostics** (Table XXVII), each with an explicitly disclosed assumption:
|
||||
|
||||
**Table XXVII.** Ten-tool unsupervised-validation collection with disclosed untested assumptions.
|
||||
|
||||
| Tool | What it measures | Untested assumption |
|
||||
|---|---|---|
|
||||
| Composition decomposition (§III-I.4; Scripts 39b–39e) | Whether descriptor multimodality is within-population (mechanism) or between-group (composition + integer artefact); $p_{\text{median}} = 0.35$ under joint firm-mean centring + integer-tie jitter | Integer-tie jitter and firm-mean centring are unbiased over the descriptor support; corroborated by Big-4 per-firm jitter (Script 39d; per-firm dHash rejection disappears under jitter at every Big-4 firm) and Big-4 pooled centred + jittered ($n_{\text{seeds}} = 5$; Script 39e) |
|
||||
| Per-comparison inter-CPA coincidence rate (§III-L.1; Script 40b) | Pair-level specificity proxy under a random-pair negative anchor | Inter-CPA pairs are negative (i.e., not template-related); partially violated by within-firm sharing (§III-L.4) |
|
||||
| Pool-normalised per-signature ICCR (§III-L.2; Script 43) | Deployed-rule specificity proxy at per-signature unit, accounting for pool size | Same as above + that pool replacement preserves the negative-anchor property |
|
||||
| Document-level ICCR (§III-L.3; Script 45) | Operational alarm rate proxy at per-document unit under three alarm definitions | Same as above |
|
||||
| Firm-heterogeneity logistic regression (§III-L.4; Script 44) | Multiplicative effect of firm membership on per-signature rate, controlling for pool size | Per-signature observations are clustered by CPA/firm; naïve standard errors inflated; cluster-robust analysis is a future check |
|
||||
| Cross-firm hit matrix (§III-L.4; Script 44) | Concentration of inter-CPA collisions within source firm | Concentration depends on deployed-rule semantics (the stricter same-pair joint event yields $97.0$–$99.96\%$ within-firm at all four firms versus $76.7$–$98.8\%$ under any-pair; §III-L.4); per-document per-firm assignment uses Script 45's mode-of-firms tie-break (§IV-M.4 footnote) |
|
||||
| Alert-rate sensitivity sweep (§III-L.5; Script 46) | Local sensitivity of deployed rule to threshold perturbation | Gradient comparison is descriptive, not a formal plateau test |
|
||||
| Convergent score Spearman ranking (§III-K.1; Script 38) | Internal-consistency of three feature-derived per-CPA scores | Scores share underlying inputs and are not statistically independent |
|
||||
| Pixel-identical conservative positive capture (§III-K.4; v3.x; Script 40) | Trivial sanity check on the conservative positive anchor | Anchor is tautologically captured by any reasonable threshold |
|
||||
| LOOO firm-level reproducibility (§III-K.3; Scripts 36, 37) | Algorithmic stability of K=2 / K=3 partition across firm folds | Stability is necessary but not sufficient for classification validity |
|
||||
|
||||
No single tool in this collection provides ground-truth validation. Their conjunction constitutes the unsupervised validation ceiling that the v4.0 corpus admits.
|
||||
|
||||
**What v4.0 does not claim.** We do not claim a validated forensic detector or an autonomous classification system. We do not report False Rejection Rate, sensitivity, recall, EER, ROC-AUC, precision, or positive predictive value against ground truth, because no ground truth exists at the signature level. We do not interpret the deployed-rate excess of §III-L.6 as a presumed true-positive rate: that interpretation would require assuming that the within-firm same-CPA pool's collision rate equals the inter-CPA proxy rate in the absence of replication (i.e., that genuine same-CPA hand-signing would produce a collision rate no higher than random inter-CPA pairs). Two factors make the assumption unsafe: (a) a CPA who signs consistently can produce stylistically similar signatures across years that exceed inter-CPA similarity at the cosine axis; (b) within-firm template sharing (§III-L.4 cross-firm hit matrix; v3.x byte-level evidence of Firm A's pixel-identical signatures across partners) places a substantial inter-CPA collision floor that itself reflects template-like reuse rather than independent inter-CPA random matching. We do not infer that the within-firm collision concentration of §III-L.4 constitutes deliberate template sharing; we describe it as "inter-CPA collision concentration is within-firm" and treat the mechanism as an open empirical question.
|
||||
|
||||
**What v4.0 does claim.** The deployed signature-replication screening rule is characterised at three units of analysis (per-comparison, per-signature pool, per-document) against an inter-CPA negative-anchor coincidence-rate calibration. The per-comparison rates ($\leq 0.0006$ at cos $> 0.95$; $\leq 0.0013$ at dHash $\leq 5$; $\leq 0.00014$ jointly) are specificity-proxy-anchored operating points consistent with biometric-verification convention, with the proxy nature recorded in §III-L.0 and §III-M. The per-signature and per-document rates ($0.11$ and $0.34$ respectively under the deployed any-pair HC + MC alarm) are operationally meaningful **alarm-yield** indicators rather than true error rates. Per-firm rates show substantial heterogeneity (Firm A's per-document HC + MC alarm at $0.62$ vs Firm B/C/D at $0.09$–$0.16$), driven by firm-level rather than pool-size effects, and concentrated in within-firm cross-CPA candidate matching. The framework is positioned as a **specificity-proxy-anchored screening tool with human-in-the-loop review**, not as a validated forensic classifier.
|
||||
**Scope of the present analysis.** The deployed signature-replication screening rule is characterised at three units of analysis (per-comparison, per-signature pool, per-document) against an inter-CPA negative-anchor coincidence-rate calibration. The per-comparison rates ($\leq 0.0006$ at cos $> 0.95$; $\leq 0.0013$ at dHash $\leq 5$; $\leq 0.00014$ jointly) are specificity-proxy-anchored operating points consistent with biometric-verification convention, with the proxy nature recorded in §III-L.0 and §III-M. The per-signature any-pair HC ICCR ($0.11$; Table XXII) and per-document HC+MC alarm-rate ICCR ($0.34$; Table XXIII) are operationally meaningful **alarm-yield** indicators rather than true error rates. Per-firm rates show substantial heterogeneity (Firm A's per-document HC + MC alarm at $0.62$ vs Firm B/C/D at $0.09$–$0.16$), driven by firm-level rather than pool-size effects, and concentrated in within-firm cross-CPA candidate matching. The framework is positioned as a **specificity-proxy-anchored screening tool with human-in-the-loop review**, not as a validated forensic classifier.
|
||||
|
||||
**Specificity-alert-yield tradeoff.** Because sensitivity is unobservable, stakeholders cannot derive an operating point by optimising a ROC criterion. Instead, the specificity-proxy-anchored framework offers a *specificity-alert-yield tradeoff*: tighter operating points (e.g., cos $> 0.98$ AND dHash $\leq 3$) reduce both per-comparison ICCR (to $\approx 5 \times 10^{-5}$; §III-L.1 inversion) and per-signature alert yield (to $\approx 0.05$; §III-L.2), with an unknown effect on actual replication-detection recall. Tighter operating points are not necessarily preferable: any tightening reduces the alert rate but may also miss true replicated signatures whose noise has pushed them outside the tighter envelope. The deployment decision depends on the relative cost of manual review (per alarm) and missed-replication risk (per false negative) — neither directly observable from corpus data.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# References
|
||||
|
||||
<!-- IEEE numbered style, sequential by first appearance in text. v3 adds statistical-method refs (37–41). -->
|
||||
<!-- IEEE numbered style, sequential by first appearance in text. -->
|
||||
|
||||
[1] Taiwan Certified Public Accountant Act (會計師法), Art. 4; FSC Attestation Regulations (查核簽證核准準則), Art. 6. Available: https://law.moj.gov.tw/ENG/LawClass/LawAll.aspx?pcode=G0400067
|
||||
|
||||
@@ -90,4 +90,4 @@
|
||||
|
||||
[44] A. Vehtari, A. Gelman, and J. Gabry, "Practical Bayesian model evaluation using leave-one-out cross-validation and WAIC," *Stat. Comput.*, vol. 27, no. 5, pp. 1413–1432, 2017.
|
||||
|
||||
<!-- Total: 44 references (v3: 41 + 3 new LOOO refs for v4 §II addition) -->
|
||||
<!-- Total: 44 references -->
|
||||
|
||||
@@ -15,8 +15,8 @@ Hafemann et al. [16] further addressed the practical challenge of adapting to ne
|
||||
A common thread in this literature is the assumption that the primary threat is *identity fraud*: a forger attempting to produce a convincing imitation of another person's signature.
|
||||
Our work addresses a fundamentally different problem---detecting whether the *legitimate signer's* stored signature image has been reproduced across many documents---which requires analyzing the upper tail of the intra-signer similarity distribution rather than modeling inter-signer discriminability.
|
||||
|
||||
Brimoh and Olisah [8] proposed a consensus-threshold approach that derives classification boundaries from known genuine reference pairs, the methodology most closely related to our calibration strategy.
|
||||
However, their method operates on standard verification benchmarks with laboratory-collected signatures, whereas our approach applies threshold calibration using a replication-dominated subpopulation identified through domain expertise in real-world regulatory documents.
|
||||
Brimoh and Olisah [8] are closest in spirit in using reference evidence to discipline threshold choice.
|
||||
Their setting, however, uses standard verification benchmarks with known genuine references, whereas our archival setting lacks signature-level labels and therefore characterises a fixed deployed screening rule through inter-CPA coincidence-rate anchors.
|
||||
|
||||
## B. Document Forensics and Copy Detection
|
||||
|
||||
@@ -51,13 +51,13 @@ Chamakh and Bounouh [22] confirmed that a simple ResNet backbone with cosine sim
|
||||
Babenko et al. [23] established that CNN-extracted neural codes with cosine similarity provide an effective framework for image retrieval and matching, a finding that underpins our feature-comparison approach.
|
||||
These findings collectively suggest that pre-trained CNN features, when L2-normalized and compared via cosine similarity, provide a robust and computationally efficient representation for signature comparison---particularly suitable for large-scale applications where the computational overhead of Siamese training or metric learning is impractical.
|
||||
|
||||
## E. Statistical Methods for Threshold Determination
|
||||
## E. Statistical Methods for Threshold Characterisation and Calibration
|
||||
|
||||
Our threshold-determination framework combines three families of methods developed in statistics and accounting-econometrics.
|
||||
Our threshold-characterisation and calibration framework combines three families of methods developed in statistics and accounting-econometrics.
|
||||
|
||||
*Non-parametric density estimation.*
|
||||
Kernel density estimation [28] provides a smooth estimate of a similarity distribution without parametric assumptions.
|
||||
Where the distribution is bimodal, the local density minimum (antimode) between the two modes is the Bayes-optimal decision boundary under equal priors.
|
||||
In idealized two-class mixture settings with equal priors and equal misclassification costs, the local density minimum (antimode) between the two modes coincides with the Bayes-optimal decision boundary.
|
||||
The statistical validity of the unimodality-vs-multimodality dichotomy can be tested via the Hartigan & Hartigan dip test [37], which tests the null of unimodality; we use rejection of this null as evidence consistent with (though not a direct test for) bimodality.
|
||||
|
||||
*Discontinuity tests on empirical distributions.*
|
||||
@@ -71,12 +71,12 @@ When the empirical distribution is viewed as a weighted sum of two (or more) lat
|
||||
For observations bounded on $[0,1]$---such as cosine similarity and normalized Hamming-based dHash similarity---the Beta distribution is the natural parametric choice, with applications spanning bioinformatics and Bayesian estimation.
|
||||
Under mild regularity conditions, White's quasi-MLE result [41] supports interpreting maximum-likelihood estimates under a mis-specified parametric family as consistent estimators of the pseudo-true parameter that minimizes the Kullback-Leibler divergence to the data-generating distribution within that family; we use this result to justify the Beta-mixture fit as a principled approximation rather than as a guarantee that the true distribution is Beta.
|
||||
|
||||
The present study combines all three families, using each to produce an independent threshold estimate and treating cross-method convergence---or principled divergence---as evidence of where in the analysis hierarchy the mixture structure is statistically supported.
|
||||
The present study uses these tools diagnostically: first to test whether the descriptor distribution supports a natural operating boundary, and then, when that support fails under composition decomposition, to motivate anchor-based ICCR calibration of a fixed deployed rule.
|
||||
|
||||
*Cross-validation in a small-cluster scope.*
|
||||
Cross-validation methodology in the leave-one-out tradition has been developed extensively in statistics since Stone [42] and Geisser [43], and modern surveys including Vehtari et al. [44] discuss its application to mixture models. In document-forensics calibration the technique has been used selectively, typically with the individual document or signature as the hold-out unit. Our application in §III-K differs in two respects from the standard usage: (i) the hold-out unit is the *firm* (not the individual CPA or signature), so the analysis directly probes cross-firm reproducibility of the fitted mixture rather than within-firm sampling variance; and (ii) the held-out predictions are interpreted as a *composition-sensitivity band* on the candidate mixture boundary, not as a sufficiency claim for the inherited five-way operational classifier (which is calibrated separately; §III-L). We treat LOOO drift as descriptive information about how the mixture characterisation moves when training composition changes, not as a pass/fail test for the operational classifier.
|
||||
Cross-validation methodology in the leave-one-out tradition has been developed extensively in statistics since Stone [42] and Geisser [43], and modern surveys including Vehtari et al. [44] discuss its application to mixture models. In document-forensics calibration the technique has been used selectively, typically with the individual document or signature as the hold-out unit. Our application in §III-K differs in two respects from the standard usage: (i) the hold-out unit is the *firm* (not the individual CPA or signature), so the analysis directly probes cross-firm reproducibility of the fitted mixture rather than within-firm sampling variance; and (ii) the held-out predictions are interpreted as a *composition-sensitivity band* on the candidate mixture boundary, not as a sufficiency claim for the deployed five-way operational classifier (§III-H.1; calibrated separately in §III-L). We treat LOOO drift as descriptive information about how the mixture characterisation moves when training composition changes, not as a pass/fail test for the operational classifier.
|
||||
<!--
|
||||
REFERENCES for Related Work (see paper_a_references_v3.md for full list):
|
||||
REFERENCES for Related Work (full list in the References section):
|
||||
[3] Bromley et al. 1993 — Siamese TDNN (NeurIPS)
|
||||
[4] Dey et al. 2017 — SigNet
|
||||
[5] Kao & Wen 2020 — Single-sample SV with forgery detection
|
||||
|
||||
+58
-58
@@ -1,6 +1,6 @@
|
||||
# IV. Experiments and Results
|
||||
|
||||
The v4.0 primary analyses (§IV-D through §IV-J) are scoped to the Big-4 sub-corpus (Firms A–D, $n = 437$ CPAs with $n_{\text{sig}} \geq 10$, totalling 150,442 signatures with both descriptors available) per the methodology choice articulated in §III-G. The §IV-K Full-Dataset Robustness section reports the full-dataset (686 CPAs) variant of the K=3 mixture + Paper A box-rule Spearman analysis as a cross-scope robustness check. §IV-A through §IV-C report inherited corpus-wide v3.x material; §IV-L (feature backbone ablation) is also inherited. §IV-M consolidates the v4-new anchor-based ICCR calibration tables.
|
||||
Section IV reports the empirical results that calibrate and characterise the operational classifier of §III-H.1 (calibration developed in §III-L). The primary analyses (§IV-D through §IV-J, and the anchor-based ICCR calibration consolidated in §IV-M) are scoped to the Big-4 sub-corpus (Firms A–D, $n = 437$ CPAs with $n_{\text{sig}} \geq 10$, totalling 150,442 signatures with both descriptors available) per the methodology choice articulated in §III-G. §IV-K reports a full-dataset (686 CPAs) robustness check on the K=3 mixture and per-CPA score-rank convergence; §IV-A through §IV-C and §IV-L report the corpus-wide pipeline performance and feature-backbone ablation that support the descriptor choice of §III-F.
|
||||
|
||||
## A. Experimental Setup
|
||||
|
||||
@@ -13,10 +13,11 @@ Because all steps rely on deterministic forward inference over fixed pre-trained
|
||||
|
||||
The YOLOv11n model achieved high detection performance on the validation set (Table II), with all loss components converging by epoch 60 and no significant overfitting despite the relatively small training set (425 images).
|
||||
We note that Table II reports validation-set metrics, as no separate hold-out test set was reserved given the small annotation budget (500 images total).
|
||||
However, the subsequent production deployment provides practical validation: batch inference on 86,071 documents yielded 182,328 extracted signatures (Table III), with an average of 2.14 signatures per document, consistent with the standard practice of two certifying CPAs per audit report.
|
||||
However, the subsequent production deployment provides a practical consistency check: batch inference on 86,071 documents yielded 182,328 extracted signatures (Table III), with an average of 2.14 signatures per document, consistent with the standard practice of two certifying CPAs per audit report.
|
||||
The high VLM--YOLO agreement rate (98.8%) further corroborates detection reliability at scale.
|
||||
|
||||
<!-- TABLE III: Extraction Results
|
||||
**Table III.** Extraction Results.
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Documents processed | 86,071 |
|
||||
@@ -25,17 +26,17 @@ The high VLM--YOLO agreement rate (98.8%) further corroborates detection reliabi
|
||||
| Avg. signatures per document | 2.14 |
|
||||
| CPA-matched signatures | 168,755 (92.6%) |
|
||||
| Processing rate | 43.1 docs/sec |
|
||||
-->
|
||||
|
||||
The Big-4 subset of the detection output yields 150,442 signatures with both descriptors (cosine and independent dHash) successfully computed; this is the per-signature population used in all §IV v4 primary analyses (§IV-D through §IV-J).
|
||||
The Big-4 subset of the detection output yields 150,442 signatures with both descriptors (cosine and independent dHash) successfully computed; this is the per-signature population used in the primary analyses of §IV-D through §IV-J.
|
||||
|
||||
## C. All-Pairs Intra-vs-Inter Class Distribution Analysis
|
||||
|
||||
Fig. 2 presents the cosine similarity distributions computed over the full set of *pairwise comparisons* under two groupings: intra-class (all signature pairs belonging to the same CPA) and inter-class (signature pairs from different CPAs).
|
||||
This all-pairs analysis is a different unit from the per-signature best-match statistics used in Sections IV-D onward; we report it first because it supplies the reference point for the KDE crossover used in per-document classification (Section III-L).
|
||||
This all-pairs analysis is a different unit from the per-signature best-match statistics used in Sections IV-D onward; we report it first because it supplies the reference point for the KDE crossover used in per-signature classification (Section III-H.1).
|
||||
Table IV summarizes the distributional statistics.
|
||||
|
||||
<!-- TABLE IV: Cosine Similarity Distribution Statistics
|
||||
**Table IV.** Cosine Similarity Distribution Statistics.
|
||||
|
||||
| Statistic | Intra-class | Inter-class |
|
||||
|-----------|-------------|-------------|
|
||||
| N (pairs) | 41,352,824 | 500,000 |
|
||||
@@ -44,14 +45,13 @@ Table IV summarizes the distributional statistics.
|
||||
| Median | 0.836 | 0.774 |
|
||||
| Skewness | −0.711 | −0.851 |
|
||||
| Kurtosis | 0.550 | 1.027 |
|
||||
-->
|
||||
|
||||
Both distributions are left-skewed and leptokurtic.
|
||||
Shapiro-Wilk and Kolmogorov-Smirnov tests rejected normality for both ($p < 0.001$), confirming that parametric thresholds based on normality assumptions would be inappropriate.
|
||||
Distribution fitting identified the lognormal distribution as the best parametric fit (lowest AIC) for both classes, though we use this result only descriptively; all subsequent threshold-estimator outputs reported in Section IV-D are derived via the methods of Section III-I to avoid single-family distributional assumptions.
|
||||
Distribution fitting identified the lognormal distribution as the best parametric fit (lowest AIC) for both classes, though we use this result only descriptively; the subsequent distributional diagnostics in Section IV-D are produced via the methods of Section III-I to avoid single-family distributional assumptions.
|
||||
|
||||
The KDE crossover---where the two density functions intersect---was located at 0.837 (Table V).
|
||||
Under equal prior probabilities and equal misclassification costs, this crossover approximates the Bayes-optimal boundary between the two classes.
|
||||
The KDE crossover---where the two density functions intersect---was located at 0.837.
|
||||
Under equal prior probabilities and equal misclassification costs, this crossover is a candidate decision boundary between the two classes; we adopt it only as the operational LH/UN boundary in §III-H.1, not as a natural distributional threshold.
|
||||
Statistical tests confirmed significant separation between the two distributions (Cohen's $d = 0.669$, Mann-Whitney [36] $p < 0.001$, K-S 2-sample $p < 0.001$).
|
||||
|
||||
We emphasize that pairwise observations are not independent---the same signature participates in multiple pairs---which inflates the effective sample size and renders $p$-values unreliable as measures of evidence strength.
|
||||
@@ -60,7 +60,7 @@ A Cohen's $d$ of 0.669 indicates a medium effect size [29], confirming that the
|
||||
|
||||
## D. Big-4 Accountant-Level Distributional Characterisation
|
||||
|
||||
This section reports the empirical evidence for §III-I's distributional diagnostics at the Big-4 accountant level. All numbers below are direct re-statements from Scripts 32 / 34. The accountant-level dip-test rejection reported in Table V is, per §III-I.4 (Scripts 39b–39e), fully attributable to between-firm location shifts and integer mass-point artefacts rather than to within-population bimodality; the v4-new composition-decomposition diagnostics that establish this finding are tabulated in §IV-M below alongside the anchor-based ICCR calibration.
|
||||
This section reports the empirical evidence for §III-I's distributional diagnostics at the Big-4 accountant level. The accountant-level dip-test rejection reported in Table V is, per §III-I.4, fully attributable to between-firm location shifts and integer mass-point artefacts rather than to within-population bimodality; the composition-decomposition diagnostics that establish this finding are tabulated in §IV-M below alongside the anchor-based ICCR calibration.
|
||||
|
||||
**Table V.** Hartigan dip-test results, accountant-level marginals (Big-4 primary; comparison scopes from Script 32).
|
||||
|
||||
@@ -82,7 +82,7 @@ Bootstrap implementation: $n_{\text{boot}} = 2000$; for the Big-4 cells, no boot
|
||||
| Firms B + C + D pooled | none | one transition at $\overline{\text{dHash}} = 10.8$ |
|
||||
| All non-Firm-A pooled | none | one transition at $\overline{\text{dHash}} = 6.6$ |
|
||||
|
||||
The Big-4-scope null on both axes is consistent with the §IV-E mixture evidence: the K=3 components overlap in their tails rather than separating sharply, so a local-discontinuity test does not flag a transition. Outside Big-4, dHash transitions appear in some subsets but no cosine transition is identified in any tested subset (Script 32 sweeps; pre-2018 and post-2020 stratified variants exhibit dHash transitions at varying locations). These off-Big-4 dHash transitions are scope-dependent and are not used as v4.0 operational thresholds; we do not claim a specific structural interpretation for them without an explicit bin-width sensitivity sweep at those scopes.
|
||||
The Big-4-scope null on both axes is consistent with the §IV-E mixture evidence: the K=3 components overlap in their tails rather than separating sharply, so a local-discontinuity test does not flag a transition. Outside Big-4, dHash transitions appear in some subsets but no cosine transition is identified in any tested subset (Script 32 sweeps; pre-2018 and post-2020 stratified variants exhibit dHash transitions at varying locations). These off-Big-4 dHash transitions are scope-dependent and are not used as operational thresholds; we do not claim a specific structural interpretation for them without an explicit bin-width sensitivity sweep at those scopes.
|
||||
|
||||
## E. Big-4 K=2 / K=3 Mixture Fits
|
||||
|
||||
@@ -122,15 +122,15 @@ This section reports the empirical evidence for §III-K's three-score internal-c
|
||||
|
||||
| Score pair | Spearman $\rho$ | $p$-value |
|
||||
|---|---|---|
|
||||
| K=3 P(C1) vs Paper A box-rule less-replication-dominated rate | $+0.9627$ | $< 10^{-248}$ |
|
||||
| Reverse-anchor cosine percentile vs Paper A box-rule less-replication-dominated rate | $+0.8890$ | $< 10^{-149}$ |
|
||||
| K=3 P(C1) vs deployed box-rule less-replication-dominated rate | $+0.9627$ | $< 10^{-248}$ |
|
||||
| Reverse-anchor cosine percentile vs deployed box-rule less-replication-dominated rate | $+0.8890$ | $< 10^{-149}$ |
|
||||
| K=3 P(C1) vs Reverse-anchor cosine percentile | $+0.8794$ | $< 10^{-142}$ |
|
||||
|
||||
(Source: Script 38.) Reverse-anchor reference: 2D Gaussian fit by MCD (support fraction 0.85) on $n = 249$ non-Big-4 CPAs; reference centre $\overline{\text{cos}} = 0.935$, $\overline{\text{dHash}} = 9.77$.
|
||||
|
||||
**Table X.** Per-firm summary across the three feature-derived scores, Big-4.
|
||||
|
||||
| Firm | $n$ CPAs | mean $P(\text{C1})$ | mean reverse-anchor score | mean Paper A less-replication-dominated rate |
|
||||
| Firm | $n$ CPAs | mean $P(\text{C1})$ | mean reverse-anchor score | mean deployed less-replication-dominated rate |
|
||||
|---|---|---|---|---|
|
||||
| Firm A | 171 | 0.0072 | $-0.9726$ | 0.1935 |
|
||||
| Firm B | 112 | 0.1410 | $-0.8201$ | 0.6962 |
|
||||
@@ -145,11 +145,11 @@ The three scores agree on placing Firm A as the most replication-dominated and t
|
||||
|
||||
| Pair | Cohen $\kappa$ |
|
||||
|---|---|
|
||||
| Paper A binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) vs per-CPA K=3 hard label | 0.662 |
|
||||
| Paper A binary high-confidence box rule vs per-signature K=3 hard label | 0.559 |
|
||||
| deployed binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) vs per-CPA K=3 hard label | 0.662 |
|
||||
| deployed binary high-confidence box rule vs per-signature K=3 hard label | 0.559 |
|
||||
| Per-CPA K=3 hard label vs per-signature K=3 hard label | 0.870 |
|
||||
|
||||
(Source: Script 39; verdict label `SIG_CONVERGENCE_MODERATE`.) Per-signature K=3 components ($n = 150{,}442$) sorted by ascending cosine: $(0.928, 9.75, 0.146)$ / $(0.963, 6.04, 0.582)$ / $(0.989, 1.27, 0.272)$, an absolute cosine drift of $0.018$ in C1 and $0.006$ in C3 relative to the per-CPA fit. These convergence checks cover only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$); the five-way classifier's moderate-confidence band ($5 < \text{dHash} \leq 15$) inherits its v3.x calibration and capture-rate evaluation (§IV-J).
|
||||
(Source: Script 39.) Per-signature K=3 components ($n = 150{,}442$) sorted by ascending cosine: $(0.928, 9.75, 0.146)$ / $(0.963, 6.04, 0.582)$ / $(0.989, 1.27, 0.272)$, an absolute cosine drift of $0.018$ in C1 and $0.006$ in C3 relative to the per-CPA fit. These convergence checks cover only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$); the five-way classifier's moderate-confidence band ($5 < \text{dHash} \leq 15$) retains its prior calibration and capture-rate evidence (supplementary materials; cross-referenced in §IV-J).
|
||||
|
||||
## G. Leave-One-Firm-Out Reproducibility
|
||||
|
||||
@@ -176,33 +176,33 @@ This section reports the firm-level cross-validation evidence motivating §III-J
|
||||
| Firm C held out | 0.9504 | 8.41 | 0.126 | $36.27\%$ | $23.53\%$ | $12.77$ pp |
|
||||
| Firm D held out | 0.9439 | 9.29 | 0.120 | $17.31\%$ | $11.54\%$ | $5.81$ pp |
|
||||
|
||||
(Source: Script 37; verdict label `P2_PARTIAL`.) Component shape is reproducible across folds: max deviation of C1 cosine = $0.005$, C1 dHash = $0.96$, C1 weight = $0.023$. Hard-posterior membership for the held-out firm varies: max absolute difference from the full-Big-4 baseline is $12.77$ pp at the Firm C held-out fold, exceeding the report's $5$ pp viability bar. We accordingly do not use K=3 hard-posterior membership as an operational classifier label (§III-J, §III-L).
|
||||
(Source: Script 37; screening label `P2_PARTIAL`.) Component shape is reproducible across folds: max deviation of C1 cosine = $0.005$, C1 dHash = $0.96$, C1 weight = $0.023$. Hard-posterior membership for the held-out firm varies: max absolute difference from the full-Big-4 baseline is $12.77$ pp at the Firm C held-out fold, exceeding the report's $5$ pp viability bar. We accordingly do not use K=3 hard-posterior membership as an operational classifier label (§III-J, §III-L).
|
||||
|
||||
## H. Pixel-Identity Positive-Anchor Miss Rate
|
||||
|
||||
This section reports the only hard-ground-truth subset analysis available in the corpus: the positive-anchor miss rate against $n = 262$ Big-4 signatures whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce pixel-identical images, so byte-identical signatures are conservative-subset ground truth for the *replicated* class. The analysis is one-sided (positive-anchor only); a paired false-alarm rate against a hand-signed negative anchor is not available because no signature-level hand-signed ground truth exists in the corpus (§III-K item 4).
|
||||
This section reports the only conservative hard-positive subset analysis available in the corpus: the positive-anchor miss rate against $n = 262$ Big-4 signatures whose nearest same-CPA match is byte-identical after crop and normalisation. Independent hand-signing cannot produce pixel-identical images, so byte-identical signatures are a conservative hard-positive subset for image replication. The analysis is one-sided (positive-anchor only); a paired false-alarm rate against a hand-signed negative anchor is not available because no signature-level hand-signed ground truth exists in the corpus (§III-K item 4).
|
||||
|
||||
**Table XIV.** Positive-anchor miss rate, $n = 262$ Big-4 byte-identical signatures.
|
||||
|
||||
| Classifier | Misclassified as less-replication-dominated | Miss rate | Wilson 95% CI |
|
||||
|---|---|---|---|
|
||||
| Paper A binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) | $0 / 262$ | $0\%$ | $[0\%, 1.45\%]$ |
|
||||
| deployed binary high-confidence box rule (cos $> 0.95$ AND dHash $\leq 5$) | $0 / 262$ | $0\%$ | $[0\%, 1.45\%]$ |
|
||||
| K=3 per-CPA hard label (C3 = high-cos / low-dHash; descriptive) | $0 / 262$ | $0\%$ | $[0\%, 1.45\%]$ |
|
||||
| Reverse-anchor (prevalence-calibrated cut) | $0 / 262$ | $0\%$ | $[0\%, 1.45\%]$ |
|
||||
|
||||
(Source: Script 40.) Per-firm breakdown of the byte-identical subset: Firm A 145; Firm B 8; Firm C 107; Firm D 2. All three candidate scores correctly assign every byte-identical signature to the replicated class.
|
||||
|
||||
We caution that for the Paper A box rule this result is close to tautological (byte-identical nearest-neighbour signatures have cosine $\approx 1$ and dHash $\approx 0$, well inside the rule's high-confidence region); v3.20.0 §V-F discusses this conservative-subset caveat at length and we retain that discussion. The reverse-anchor cut is chosen by *prevalence calibration* against the inherited box rule's overall replicated rate of $49.58\%$ across Big-4 signatures; this is a documented v4.0 limitation since no signature-level hand-signed ground truth exists to permit direct ROC optimisation.
|
||||
We caution that for the deployed box rule this result is close to tautological (byte-identical nearest-neighbour signatures have cosine $\approx 1$ and dHash $\approx 0$, well inside the rule's high-confidence region). The reverse-anchor cut is chosen by *prevalence calibration* against the box rule's overall replicated rate of $49.58\%$ across Big-4 signatures; this is a documented limitation since no signature-level hand-signed ground truth exists to permit direct ROC optimisation.
|
||||
|
||||
## I. Inter-CPA Pair-Level Coincidence Rate (Big-4 spike + inherited corpus-wide)
|
||||
## I. Inter-CPA Pair-Level Coincidence Rate
|
||||
|
||||
The signature-level inter-CPA pair-level coincidence-rate analysis (reported in v3.20.0 §IV-F.1, Table X as "FAR") is inherited and extended in v4.0. v4.0 retroactively reframes the metric as **inter-CPA pair-level coincidence rate (ICCR)** rather than "False Acceptance Rate" because the corpus does not provide signature-level ground-truth negative labels; the inter-CPA negative-anchor assumption underpinning the metric is itself partially violated by within-firm cross-CPA template-like collision structures (§III-L.4). The v3.20.0 corpus-wide spike on $\sim 50{,}000$ inter-CPA pairs reported a per-comparison rate of $0.0005$ (Wilson 95% CI $[0.0003, 0.0007]$) at the cosine cut $0.95$.
|
||||
The metric reported here is the inter-CPA pair-level coincidence rate (ICCR). It is the per-pair rate at which two signatures from different CPAs satisfy the deployed rule. We do not label it as a False Acceptance Rate because (a) FAR has a biometric-verification meaning that requires ground-truth negative labels, and (b) the inter-CPA negative-anchor assumption is partially violated by within-firm cross-CPA template-like collision structures (§III-L.4 cross-firm hit matrix).
|
||||
|
||||
v4.0 additionally reports the §III-L.1 Big-4-scope spike at higher sample size ($5 \times 10^5$ inter-CPA pairs; Script 40b), which replicates and extends the v3 result and adds the structural dimension (dHash) and joint-rule rates. The §III-L.1 numbers are referenced rather than duplicated here; the consolidated v4-new ICCR calibration appears in §IV-M Tables XXI–XXVI.
|
||||
A corpus-wide spike on $\sim 50{,}000$ inter-CPA pairs gives a per-comparison rate of $0.0005$ (Wilson 95% CI $[0.0003, 0.0007]$) at the cosine cut $0.95$. The Big-4-scope spike at higher sample size ($5 \times 10^5$ inter-CPA pairs) replicates this number, adds the structural dimension (dHash), and adds joint-rule rates; the §III-L.1 numbers are referenced rather than duplicated here, and the consolidated ICCR calibration appears in §IV-M Tables XXI–XXVI.
|
||||
|
||||
## J. Five-Way Per-Signature + Document-Level Classification Output
|
||||
|
||||
This section reports the §III-L five-way per-signature + document-level worst-case classifier output on the Big-4 sub-corpus. The five-way category definitions are inherited unchanged from v3.20.0 §III-K (now §III-L); see §III-L for the cosine and dHash cuts.
|
||||
This section reports the five-way per-signature + document-level worst-case classifier output on the Big-4 sub-corpus. See §III-H.1 for the five-way category definitions and the cosine and dHash cuts; calibration is in §III-L.
|
||||
|
||||
**Table XV.** Five-way per-signature category counts, Big-4 sub-corpus, $n = 150{,}442$ classified.
|
||||
|
||||
@@ -225,11 +225,11 @@ This section reports the §III-L five-way per-signature + document-level worst-c
|
||||
| Firm C | 23.75% | 41.44% | 0.38% | 34.21% | 0.22% | 38,613 |
|
||||
| Firm D | 24.51% | 29.33% | 0.22% | 45.65% | 0.29% | 17,133 |
|
||||
|
||||
(Source: Script 42 per-firm cross-tab.) The per-firm pattern qualitatively aligns with the K=3 cluster cross-tab of Table XVI: Firm A's signatures concentrate in the HC band (81.70%) while its CPAs concentrate at the accountant level in the K=3 C3 (high-cos / low-dHash) component (82.46%; Table XVI). These two figures address different units (per-signature classification vs per-CPA hard cluster assignment) and are not directly comparable as a like-for-like consistency check; we report the qualitative alignment but do not infer a numerical equivalence. The three non-Firm-A Big-4 firms have markedly lower HC rates than Firm A and substantially higher Uncertain rates, with Firm D having the highest Uncertain rate (45.65%).
|
||||
(Source: Script 42 per-firm cross-tab.) The per-firm pattern qualitatively aligns with the K=3 cluster cross-tab of Table XVII: Firm A's signatures concentrate in the HC band (81.70%) while its CPAs concentrate at the accountant level in the K=3 C3 (high-cos / low-dHash) component (82.46%; Table XVII). These two figures address different units (per-signature classification vs per-CPA hard cluster assignment) and are not directly comparable as a like-for-like consistency check; we report the qualitative alignment but do not infer a numerical equivalence. The three non-Firm-A Big-4 firms have markedly lower HC rates than Firm A and substantially higher Uncertain rates, with Firm D having the highest Uncertain rate (45.65%).
|
||||
|
||||
**Document-level worst-case aggregation.** Each audit report typically carries two certifying-CPA signatures. We aggregate signature-level outcomes to document-level labels using the v3.20.0 worst-case rule (HC > MC > HSC > UN > LH; §III-L). v4.0 does not change this aggregation rule; only the population over which it is computed changes (Big-4 subset).
|
||||
**Document-level worst-case aggregation.** Each audit report typically carries two certifying-CPA signatures. We aggregate signature-level outcomes to document-level labels using the worst-case rule (HC > MC > HSC > UN > LH; §III-H.1), applied to the Big-4 sub-corpus.
|
||||
|
||||
**Table XIX.** Document-level worst-case category counts, Big-4 sub-corpus, $n = 75{,}233$ unique PDFs.
|
||||
**Table XVI.** Document-level worst-case category counts, Big-4 sub-corpus, $n = 75{,}233$ unique PDFs.
|
||||
|
||||
| Category | Long name | $n$ documents | % |
|
||||
|---|---|---|---|
|
||||
@@ -252,9 +252,9 @@ This section reports the §III-L five-way per-signature + document-level worst-c
|
||||
|
||||
(Source: Script 42; mixed-firm PDFs $n = 379$ excluded from the per-firm rows but included in the overall counts above.)
|
||||
|
||||
The five-way **moderate-confidence non-hand-signed** band (cos $> 0.95$ AND $5 < \text{dHash} \leq 15$) inherits its v3.x calibration; it is **not separately validated by Scripts 38–40**, which evaluated only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$). v4.0 does not re-derive the moderate-band cuts on the Big-4 subset; we report the Table XV per-firm MC proportions (10.76% / 35.88% / 41.44% / 29.33% across Firms A through D) descriptively. The v3.20.0 capture-rate calibration evidence for the moderate band (v3.20.0 Tables IX, XI, XII, XII-B) is carried into v4.0 by reference and not regenerated on the Big-4 subset. We do not claim that the MC-band per-firm ordering above is a separate validation of the §III-K Spearman convergence, since MC occupancy is not a monotone function of the per-CPA less-replication-dominated ranking (e.g., Firm D's MC fraction is lower than Firm B's while Firm D's reverse-anchor score ranks it as less replication-dominated than Firm B).
|
||||
The five-way **moderate-confidence non-hand-signed** band (cos $> 0.95$ AND $5 < \text{dHash} \leq 15$) retains its prior calibration (supplementary materials); it is **not separately re-characterised by Scripts 38–40**, which checked only the binary high-confidence rule (cos $> 0.95$ AND dHash $\leq 5$). The moderate-band cuts are not re-derived on the Big-4 subset; we report the Table XV per-firm MC proportions (10.76% / 35.88% / 41.44% / 29.33% across Firms A through D) descriptively. The capture-rate calibration evidence for the moderate band is reported in the supplementary materials and not regenerated on the Big-4 subset. We do not claim that the MC-band per-firm ordering above is a separate validation of the §III-K Spearman convergence, since MC occupancy is not a monotone function of the per-CPA less-replication-dominated ranking (e.g., Firm D's MC fraction is lower than Firm B's while Firm D's reverse-anchor score ranks it as less replication-dominated than Firm B).
|
||||
|
||||
**Table XVI.** Firm × K=3 cluster cross-tabulation, Big-4 sub-corpus.
|
||||
**Table XVII.** Firm × K=3 cluster cross-tabulation, Big-4 sub-corpus.
|
||||
|
||||
| Firm | $n$ | C1 (low-cos / high-dHash) | C2 (central) | C3 (high-cos / low-dHash) | C1 % | C3 % |
|
||||
|---|---|---|---|---|---|---|
|
||||
@@ -265,13 +265,13 @@ The five-way **moderate-confidence non-hand-signed** band (cos $> 0.95$ AND $5 <
|
||||
|
||||
(Source: Script 35.) The cross-tab is the accountant-level descriptive output of the K=3 mixture (§III-J / §IV-E). It is reported here as a complement to the five-way per-signature classifier (Table XV), not as an operational classifier output. Reading: Firm A's CPAs are concentrated in the C3 (high-cos / low-dHash) component (no Firm A CPAs in C1); Firm C has the highest C1 (low-cos / high-dHash) concentration of the Big-4 (C1 fraction $23.5\%$); Firms B and D sit between A and C on the K=3 hard-label ordering, broadly consistent with the per-firm Spearman ordering of Table X (with the within-Big-4-non-A reverse-anchor disagreement noted there).
|
||||
|
||||
**Document-level worst-case aggregation outputs are reported in Table XIX above.**
|
||||
**Document-level worst-case aggregation outputs are reported in Table XVI above.**
|
||||
|
||||
## K. Full-Dataset Robustness (light scope)
|
||||
|
||||
This section reports the v4.0 reproducibility cross-check at the full accountant scope ($n = 686$ CPAs, Big-4 plus mid/small firms). The scope of §IV-K is deliberately narrow: we re-run only the K=3 mixture + Paper A operational-rule per-CPA less-replication-dominated rate analysis, sufficient to demonstrate that the v4.0 K=3 + Paper A convergence reproduces at the wider scope. The §III-L five-way classifier and the §IV-G LOOO analyses are not re-run at the full scope. The five-way moderate-confidence band is documented as inherited from v3.x calibration in §IV-J.
|
||||
This section reports the reproducibility cross-check at the full accountant scope ($n = 686$ CPAs, Big-4 plus mid/small firms). The scope of §IV-K is deliberately narrow: we re-run only the K=3 mixture + deployed operational-rule per-CPA less-replication-dominated rate analysis, sufficient to demonstrate that the K=3 + deployed-rule convergence reproduces at the wider scope. The §III-H.1 five-way classifier and the §IV-G LOOO analyses are not re-run at the full scope. The five-way moderate-confidence band retains its prior calibration (supplementary materials; §IV-J).
|
||||
|
||||
**Table XVII.** K=3 component comparison, Big-4 sub-corpus vs full dataset.
|
||||
**Table XVIII.** K=3 component comparison, Big-4 sub-corpus vs full dataset.
|
||||
|
||||
| K=3 component | Big-4 (n=437) cos / dHash / weight | Full (n=686) cos / dHash / weight | Drift Big-4 → Full |
|
||||
|---|---|---|---|
|
||||
@@ -281,9 +281,9 @@ This section reports the v4.0 reproducibility cross-check at the full accountant
|
||||
|
||||
(Source: Script 41; full-dataset $\text{BIC}(K{=}3) = -792.31$ vs Big-4 $\text{BIC}(K{=}3) = -1111.93$; BIC values are not directly comparable across different $n$ and are reported only for completeness.)
|
||||
|
||||
**Table XVIII.** Spearman rank correlation between K=3 P(C1) and Paper A operational less-replication-dominated rate, Big-4 sub-corpus vs full dataset.
|
||||
**Table XIX.** Spearman rank correlation between K=3 P(C1) and deployed operational less-replication-dominated rate, Big-4 sub-corpus vs full dataset.
|
||||
|
||||
| Scope | $n$ CPAs | Spearman $\rho$ (P(C1) vs Paper A less-replication-dominated rate) | $p$-value |
|
||||
| Scope | $n$ CPAs | Spearman $\rho$ (P(C1) vs deployed less-replication-dominated rate) | $p$-value |
|
||||
|---|---|---|---|
|
||||
| Big-4 (primary) | 437 | $+0.9627$ | $< 10^{-248}$ |
|
||||
| Full dataset | 686 | $+0.9558$ | $< 10^{-300}$ |
|
||||
@@ -291,15 +291,15 @@ This section reports the v4.0 reproducibility cross-check at the full accountant
|
||||
|
||||
(Source: Script 41.)
|
||||
|
||||
**Reading.** The K=3 component ordering and the strong Spearman convergence between K=3 P(C1) and the Paper A box-rule less-replication-dominated rate are preserved at the full scope. Component centres shift modestly: C3 (high-cos / low-dHash) is essentially unchanged in centre but loses weight $0.117$ as the full population includes more non-templated CPAs (mid/small firms); C1 (low-cos / high-dHash) gains weight $0.141$ and shifts to lower cosine and higher dHash (centre $(0.928, 11.17)$ vs Big-4 $(0.946, 9.17)$) as the broader population includes mid/small-firm CPAs landing toward the low-cos / high-dHash region that the Big-4-primary scope deliberately excludes. We read this as evidence that the Big-4-primary K=3 + Paper A convergence is not a Big-4-specific artefact; we do **not** read it as an endorsement of using full-dataset K=3 component centres or operational thresholds in place of the Big-4-primary analysis. Mid/small-firm composition shifts the component centres meaningfully and the v4.0 primary methodology is restricted to Big-4 by design (§III-G item 4).
|
||||
**Reading.** The K=3 component ordering and the strong Spearman convergence between K=3 P(C1) and the deployed box-rule less-replication-dominated rate are preserved at the full scope. Component centres shift modestly: C3 (high-cos / low-dHash) is essentially unchanged in centre but loses weight $0.117$ as the full population includes more non-templated CPAs (mid/small firms); C1 (low-cos / high-dHash) gains weight $0.141$ and shifts to lower cosine and higher dHash (centre $(0.928, 11.17)$ vs Big-4 $(0.946, 9.17)$) as the broader population includes mid/small-firm CPAs landing toward the low-cos / high-dHash region that the Big-4-primary scope deliberately excludes. We read this as evidence that the Big-4-primary K=3 + deployed-rule convergence is not a Big-4-specific artefact; we do **not** read it as an endorsement of using full-dataset K=3 component centres or operational thresholds in place of the Big-4-primary analysis. Mid/small-firm composition shifts the component centres meaningfully and the primary methodology is restricted to Big-4 by design (§III-G item 4).
|
||||
|
||||
## L. Ablation Study: Feature Backbone Comparison
|
||||
|
||||
To validate the choice of ResNet-50 as the feature extraction backbone, we conducted an ablation study comparing three pre-trained architectures: ResNet-50 (2048-dim), VGG-16 (4096-dim), and EfficientNet-B0 (1280-dim).
|
||||
To support the choice of ResNet-50 as the feature extraction backbone, we conducted an ablation study comparing three pre-trained architectures: ResNet-50 (2048-dim), VGG-16 (4096-dim), and EfficientNet-B0 (1280-dim).
|
||||
All models used ImageNet pre-trained weights without fine-tuning, with identical preprocessing and L2 normalization.
|
||||
The comparison summary is inherited unchanged from the v3.20.0 backbone-ablation table (v3.20.0 Table XVIII; not the same table as v4 Table XVIII which reports Big-4 vs full-dataset Spearman drift in §IV-K).
|
||||
The comparison summary is reported in the supplementary materials (backbone-ablation table; not the same table as Table XIX in this section, which reports Big-4 vs full-dataset Spearman drift in §IV-K).
|
||||
|
||||
<!-- v3.20.0 TABLE XVIII (inherited unchanged; reproduced here in commented form for v4 splice provenance — actual rendered table is the v3.20.0 backbone-ablation table):
|
||||
<!-- BACKBONE ABLATION TABLE (rendered in supplementary materials):
|
||||
| Metric | ResNet-50 | VGG-16 | EfficientNet-B0 |
|
||||
|--------|-----------|--------|-----------------|
|
||||
| Feature dim | 2048 | 4096 | 1280 |
|
||||
@@ -319,18 +319,18 @@ single closest match from the same CPA.
|
||||
-->
|
||||
|
||||
EfficientNet-B0 achieves the highest Cohen's $d$ (0.707), indicating the greatest statistical separation between intra-class and inter-class distributions.
|
||||
However, it also exhibits the widest distributional spread (intra std $= 0.123$ vs. ResNet-50's $0.098$), resulting in lower per-sample classification confidence.
|
||||
However, it also exhibits the widest distributional spread (intra std $= 0.123$ vs. ResNet-50's $0.098$), i.e., a wider descriptor dispersion per signature.
|
||||
VGG-16 performs worst on all key metrics despite having the highest feature dimensionality (4096), suggesting that additional dimensions do not contribute discriminative information for this task.
|
||||
|
||||
ResNet-50 provides the best overall balance:
|
||||
(1) Cohen's $d$ of 0.669 is competitive with EfficientNet-B0's 0.707;
|
||||
(2) its tighter distributions yield more reliable individual classifications;
|
||||
(3) the highest Firm A all-pairs 1st percentile (0.543) indicates that known-replication signatures are least likely to produce low-similarity outlier pairs under this backbone; and
|
||||
(2) its tighter distributions yield more stable descriptor behaviour at the per-signature level;
|
||||
(3) the highest Firm A all-pairs 1st percentile (0.543) indicates that Firm A replication-dominated signatures are least likely to produce low-similarity outlier pairs under this backbone; and
|
||||
(4) its 2048-dimensional features offer a practical compromise between discriminative capacity and computational/storage efficiency for processing 182K+ signatures.
|
||||
|
||||
## M. v4-New Anchor-Based ICCR Calibration Results
|
||||
## M. Anchor-Based ICCR Calibration Results
|
||||
|
||||
This section consolidates the v4-new empirical results that support the §III-L anchor-based threshold calibration framework. Numbers below are direct re-statements from the spike scripts cited per row; the corresponding §III provenance table entries appear in §III's provenance table.
|
||||
This section consolidates the empirical results that support the §III-L anchor-based threshold calibration framework.
|
||||
|
||||
### M.1 Composition decomposition (Scripts 39b–39e)
|
||||
|
||||
@@ -342,29 +342,29 @@ This section consolidates the v4-new empirical results that support the §III-L
|
||||
| Within-firm signature-level cosine dip | non-Big-4 (10 firms $\geq 500$ sigs) | $p_{\text{cos}} \in [0.59, 0.99]$ | 0/10 firms reject; cosine within-firm unimodal |
|
||||
| Within-firm jittered-dHash dip (5 seeds, median) | Big-4 (4 firms) | $p_{\text{med}} \in \{0.999, 0.996, 0.999, 0.9995\}$ | 0/4 firms reject after integer-jitter; raw rejection was integer-tie artefact |
|
||||
| Big-4 pooled dHash: 2×2 factorial | firm-centred + jittered (5 seeds) | $p_{\text{med}} = 0.35$, 0/5 seeds reject | combined corrections eliminate rejection; multimodality is composition + integer artefact |
|
||||
| Integer-histogram valley near $\text{dHash} \approx 5$ | within each Big-4 firm | none (0/4 firms) | no within-firm dHash antimode at the inherited HC cutoff |
|
||||
| Integer-histogram valley near $\text{dHash} \approx 5$ | within each Big-4 firm | none (0/4 firms) | no within-firm dHash antimode at the deployed HC cutoff |
|
||||
|
||||
(Source: Scripts 39b, 39c, 39d, 39e; bootstrap $n_{\text{boot}} = 2000$; jitter $\sim \mathrm{U}[-0.5, +0.5]$.)
|
||||
|
||||
### M.2 Anchor-based inter-CPA pair-level ICCR (Script 40b)
|
||||
|
||||
**Table XXI.** Big-4 inter-CPA per-comparison ICCR sweep, $n = 5 \times 10^5$ pairs (Big-4 scope; v4 new).
|
||||
**Table XXI.** Big-4 inter-CPA per-comparison ICCR sweep, $n = 5 \times 10^5$ pairs (Big-4 scope).
|
||||
|
||||
| Threshold | Per-comparison ICCR | 95% Wilson CI |
|
||||
|---|---|---|
|
||||
| cos $> 0.945$ (v3.x published "natural threshold") | $0.00081$ | $[0.00073, 0.00089]$ |
|
||||
| cos $> 0.95$ (inherited operating point) | $0.00060$ | $[0.00053, 0.00067]$ |
|
||||
| cos $> 0.945$ (alternative operating point from supplementary calibration evidence) | $0.00081$ | $[0.00073, 0.00089]$ |
|
||||
| cos $> 0.95$ (deployed operating point) | $0.00060$ | $[0.00053, 0.00067]$ |
|
||||
| cos $> 0.97$ | $0.00024$ | $[0.00020, 0.00029]$ |
|
||||
| cos $> 0.98$ | $0.00009$ | $[0.00007, 0.00012]$ |
|
||||
| dHash $\leq 5$ (inherited operating point) | $0.00129$ | $[0.00120, 0.00140]$ |
|
||||
| dHash $\leq 5$ (deployed operating point) | $0.00129$ | $[0.00120, 0.00140]$ |
|
||||
| dHash $\leq 4$ | $0.00050$ | $[0.00044, 0.00057]$ |
|
||||
| dHash $\leq 3$ | $0.00019$ | $[0.00015, 0.00023]$ |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 5$ (any-pair semantics) | $0.00014$ | — |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 4$ (any-pair) | $0.00011$ | — |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 5$ (any-pair semantics) | $0.00014$ | $[0.00011, 0.00018]$ |
|
||||
| Joint: cos $> 0.95$ AND dHash $\leq 4$ (any-pair) | $0.00011$ | $[0.00008, 0.00014]$ |
|
||||
|
||||
Conditional ICCR(dHash $\leq 5$ | cos $> 0.95$) $= 0.234$ (Wilson 95% $[0.190, 0.285]$; $70$ of $299$ pairs).
|
||||
|
||||
The cos $> 0.95$ row replicates v3.20.0 §IV-F.1 Table X (v3 reported $0.0005$ under prior "FAR" terminology). The dHash row and joint row are v4 new.
|
||||
The cos $> 0.95$ row is consistent with the corpus-wide spike of §IV-I (per-comparison rate $0.0005$). The dHash row and joint row are reported here for the first time on this corpus.
|
||||
|
||||
### M.3 Pool-normalised per-signature ICCR (Script 43)
|
||||
|
||||
@@ -393,7 +393,7 @@ Decile trend is broadly monotone in pool size with two minor reversals (decile 5
|
||||
| D2 (operational) | HC + MC | $0.3375$ | $[0.3342, 0.3409]$ |
|
||||
| D3 | HC + MC + HSC | $0.3384$ | $[0.3351, 0.3418]$ |
|
||||
|
||||
Per-firm D2 document-level ICCR: Firm A $0.6201$ ($n = 30{,}226$); Firm B $0.1600$ ($n = 17{,}127$); Firm C $0.1635$ ($n = 19{,}501$); Firm D $0.0863$ ($n = 8{,}379$). The Firm C denominator $n = 19{,}501$ exceeds Table XIX's single-firm Firm C count of $19{,}122$ by exactly the $379$ mixed-firm PDFs: all $379$ are $1{:}1$ Firm C / Firm D mixed-firm documents, and Script 45's mode-of-firms implementation (`np.argmax` over `np.unique`'s alphabetically-sorted firm counts) returns the first-sorted firm on ties, which assigns these tied documents to Firm C rather than to Firm D. The four per-firm denominators here therefore sum to the full $75{,}233$, whereas Table XIX's per-firm rows sum to $74{,}854 = 75{,}233 - 379$.
|
||||
Per-firm D2 document-level ICCR: Firm A $0.6201$ ($n = 30{,}226$); Firm B $0.1600$ ($n = 17{,}127$); Firm C $0.1635$ ($n = 19{,}501$); Firm D $0.0863$ ($n = 8{,}379$). The Firm C denominator $n = 19{,}501$ exceeds Table XVI's single-firm Firm C count of $19{,}122$ by exactly the $379$ mixed-firm PDFs (all $379$ are $1{:}1$ Firm C / Firm D documents that an alphabetically-ordered tie-break assigns to Firm C; full implementation detail in the supplementary materials). The four per-firm denominators here therefore sum to the full $75{,}233$, whereas Table XVI's per-firm rows sum to $74{,}854 = 75{,}233 - 379$.
|
||||
|
||||
### M.5 Firm heterogeneity logistic regression and cross-firm hit matrix (Script 44)
|
||||
|
||||
@@ -419,9 +419,9 @@ Per-decile per-firm rates (Table not duplicated here; Script 44 decile table ava
|
||||
|
||||
Same-pair joint hits (single candidate satisfying both cos $> 0.95$ AND dHash $\leq 5$) are within-firm at rates $99.96\%$ / $97.7\%$ / $98.2\%$ / $97.0\%$ for Firms A/B/C/D respectively.
|
||||
|
||||
### M.6 Alert-rate sensitivity around inherited HC threshold (Script 46)
|
||||
### M.6 Alert-rate sensitivity around deployed HC threshold (Script 46)
|
||||
|
||||
**Table XXVI.** Local-gradient / median-gradient ratio at inherited thresholds (descriptive plateau diagnostic).
|
||||
**Table XXVI.** Local-gradient / median-gradient ratio at deployed thresholds (descriptive plateau diagnostic).
|
||||
|
||||
| Threshold | Local / median gradient ratio | Interpretation |
|
||||
|---|---|---|
|
||||
@@ -429,4 +429,4 @@ Same-pair joint hits (single candidate satisfying both cos $> 0.95$ AND dHash $\
|
||||
| dHash $= 5$ (HC) | $\approx 3.8\times$ | locally sensitive (not plateau-stable) |
|
||||
| dHash $= 15$ (MC/HSC boundary) | $\approx 0.08$ | plateau-like (saturating tail) |
|
||||
|
||||
Big-4 observed deployed alert rate on actual same-CPA pools: per-signature HC $= 0.4958$; per-document HC $= 0.6228$. The deployed-rate excess over the inter-CPA proxy is $0.3856$ pp per-signature and $0.4431$ pp per-document; this excess is interpreted as a same-CPA repeatability signal under the §III-M caveats, not as a presumed true-positive rate.
|
||||
Big-4 observed deployed alert rate on actual same-CPA pools: per-signature HC $= 0.4958$; per-document HC $= 0.6228$. The deployed-rate excess over the inter-CPA proxy is $0.3856$ ($38.6$ pp) per-signature and $0.4431$ ($44.3$ pp) per-document; this excess is reported as an observed same-CPA-pool excess under §III-M caveats, not as a presumed true-positive rate and not attributed to within-CPA handwriting repeatability.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
# Canonical number set — Paper A v13 rev9.1 (verified 2026-06-30)
|
||||
|
||||
Source of truth: `signature_analysis.db` (`/Volumes/NV2/PDF-Processing/signature-analysis/`).
|
||||
Verified against scripts in `paper/v13_build/scripts/`. All firm-level signature counts and
|
||||
within-accountant HC rates below are DB-reproduced to 2 decimals.
|
||||
|
||||
## ROOT CAUSE of the "Firm C/D two-snapshot" inconsistency
|
||||
|
||||
It is **NOT** a stale snapshot. It is **two live firm-assignment keys** used inconsistently:
|
||||
|
||||
| Key | Definition | C | D | total |
|
||||
|---|---|---|---|---|
|
||||
| **assigned** (canonical) | `assigned_accountant → accountants.firm` (registry firm) | 38,613 | 17,133 | **150,442** |
|
||||
| excel | `excel_firm` column (source Excel metadata) | 38,993 | 16,752 | 150,441 |
|
||||
|
||||
Difference = **379 signatures with excel_firm=資誠(C) but registry firm=安永(D)** (one/few
|
||||
cross-labeled accountants). A and B are identical under both keys. Because the headline
|
||||
contrast pools B/C/D, the swap is invisible to it (FE/LOYO/bootstrap reproduce exactly under
|
||||
either key); only **per-firm-separated tables** (IV, II-b, II-c, VI) are affected.
|
||||
|
||||
DECISION: standardize on the **assigned** key — it is what headline Table IV/II-b already use.
|
||||
|
||||
## "Analyzable signatures" definition (reproduces 150,442 exactly)
|
||||
|
||||
Analyzable = signature whose CPA has **≥2 signatures** (singleton CPAs have no same-accountant
|
||||
partner). Per-firm singletons (A:2, B:6, C:3, D:0) exactly equal raw−Set A.
|
||||
|
||||
## Canonical signature counts (assigned key)
|
||||
|
||||
| Firm | Full 2013–23 | 2013–19 | 2020–23 |
|
||||
|---|---|---|---|
|
||||
| A | 60,448 | 36,550 | 23,898 |
|
||||
| B | 34,248 | 19,677 | 14,571 |
|
||||
| C | 38,613 | 22,449 | 16,164 |
|
||||
| D | 17,133 | 9,945 | 7,188 |
|
||||
| Big-4 | **150,442** | 88,621 | 61,821 |
|
||||
|
||||
Every row sums exactly. (excel-key alternatives, do NOT use for per-firm tables:
|
||||
C 2020–23 = 16,485, D 2020–23 = 6,866 — these are what the stale Table II-c rows show.)
|
||||
|
||||
## Five-way breakdown (HC | MC | HSC | UN | LH | n), low cut = 0.8547
|
||||
|
||||
FULL 2013–2023 (Table IV):
|
||||
- A: 81.70 | 10.76 | 0.05 | 7.35 | 0.14 | 60,448
|
||||
- B: 34.56 | 35.88 | 0.29 | 28.95 | 0.32 | 34,248
|
||||
- C: 23.75 | 41.44 | 0.38 | 33.97 | 0.47 | 38,613
|
||||
- D: 24.51 | 29.33 | 0.22 | 45.28 | 0.66 | 17,133
|
||||
- Overall: 49.58 | 26.47 | 0.21 | 23.42 | 0.32 | 150,442
|
||||
|
||||
2013–2019 (Table II-b):
|
||||
- B: 29.04 | 39.31 | 0.39 | 30.91 | 0.35 | 19,677
|
||||
- C: 21.59 | 42.09 | 0.37 | 35.53 | 0.43 | 22,449
|
||||
- D: 22.01 | 29.67 | 0.20 | 47.35 | 0.76 | 9,945
|
||||
|
||||
2020–2023 (Table II-c) — **C/D are the fix**:
|
||||
- A: 83.84 | 9.13 | 0.04 | 6.88 | 0.11 | 23,898
|
||||
- B: 42.01 | 31.24 | 0.16 | 26.31 | 0.28 | 14,571
|
||||
- C: **26.74 | 40.55 | 0.40 | 31.80 | 0.51 | 16,164** (manuscript stale: 26.53/.../16,485)
|
||||
- D: **27.98 | 28.85 | 0.24 | 42.42 | 0.51 | 7,188** (manuscript stale: 28.53/.../6,866)
|
||||
|
||||
Per-firm period HC rates (§IV-C text / Fig 5 — already CORRECT in manuscript):
|
||||
A 80.3→83.8, B 29.0→42.0, C 21.6→26.7, D 22.0→28.0.
|
||||
|
||||
## Table VI (any-pair vs same-pair HC) under assigned key
|
||||
|
||||
| firm | n | any-pair% | same-pair% |
|
||||
|---|---|---|---|
|
||||
| A | 60,448 | 81.7 | 57.3 |
|
||||
| B | 34,248 | 34.6 | 9.0 |
|
||||
| C | 38,613 | 23.7 | 5.3 |
|
||||
| D | 17,133 | 24.5 | 7.7 |
|
||||
| all | 150,442 | 49.6 | 27.3 |
|
||||
|
||||
(Manuscript currently shows excel-key: C 38,993, D 16,752, all 150,441, D any-pair 24.7.)
|
||||
|
||||
## Accountant counts (R2) — reviewer's "179 > 171 impossible" is a FALSE POSITIVE
|
||||
|
||||
Different keys, not a contradiction:
|
||||
- Bootstrap/FE (`accountant_id`, is_valid): **A=179** (reproduces exactly), BCD=281 (manuscript 280).
|
||||
- Table III / accountant-level partition (171/112/102/52 = 437): RESOLVED 2026-06-30 — this is the
|
||||
Big-4 accountants with **≥10 signatures** (registry key), reproduces EXACTLY on current DB.
|
||||
GMM recipe: accountant point = (mean max_similarity_to_same_accountant, mean min_dhash_independent);
|
||||
sklearn GaussianMixture(n_components=3, covariance_type='full', random_state=42, n_init=10);
|
||||
templated cluster = highest-cosine component (cos 0.983 / dHash 2.4). Shares reproduce to the digit:
|
||||
A=82.5% (141/171), B=0.0% (0/112), C=1.0% (1/102), D=1.9% (1/52). Script: scratchpad/gmm.py.
|
||||
- Analyzable accountant count (registry name key, ≥2 sig) = 457 (A=178, B=119, C=107, D=53) — owns
|
||||
the 150,442 signatures; now stated in Table I and the §III-B prose.
|
||||
|
||||
THREE DISTINCT, ALL-REPRODUCIBLE accountant universes (the reviewer collapsed them → false "179>171"):
|
||||
457 (≥2 sig, owns 150,442) | 437 (≥10 sig, Table III GMM) | 459 = 179/280 (accountant_id bootstrap).
|
||||
Manuscript edits applied 2026-06-30: Table I + §III-B prose → 457; §III-B prose + Table III caption
|
||||
note the 437/≥10-sig subset. Bootstrap 179/280 stays (correct, key-invariant).
|
||||
|
||||
## F5 robustness (re-validated EXACTLY on current DB, key-invariant)
|
||||
Firm+Year FE ORs: B=0.116, C=0.061, D=0.070. LOYO gap range [53.1, 54.9]pp, full-sample 53.7pp.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
@@ -0,0 +1,499 @@
|
||||
# Anchor-Calibrated, Label-Free Screening of Non-Hand-Signed Signatures in Large-Scale Audit Reports
|
||||
|
||||
*(Authors removed for double-blind review)*
|
||||
|
||||
## Abstract
|
||||
|
||||
An audit report must carry each certifying accountant's signature as the mark of an individual act of endorsement, yet in digital workflows a saved image can instead be pasted onto many reports — by stamping or by an automated signing system — producing what we term non-hand-signed signatures. The signer is genuine; the question is whether signing occurred for each report, and at archive scale this carries no labels. We present a label-free screening system and apply it to 86,071 Taiwanese statutory audit reports (2013–2023), within which the four largest firms contribute 150,442 analyzable signatures. The system finds the signature page, detects signatures, extracts deep features, and computes two similarities to the same accountant's other signatures — a style cosine and a perceptual-hash (dHash) structural distance — on the logic that a consistent hand keeps style high while structure varies, whereas a reused image keeps both extreme. With no labels and no natural gap in the data (a unimodality test gives median p = 0.35 once firm effects and the hash's integer steps are removed), no cutoff can be learned; instead we calibrate a five-way rule by how often it fires by chance between unrelated accountants in a clean reference group (non-Firm-A firms, 2013–2019), where the strict rule fires on about 1.2% of reports and a looser advisory band on about 17.5%. Held out as a known-positive benchmark — one firm independently described by interviews as a stamping firm (a confirmatory check, not a blind test) — it fires the strict rule on 82% of its own signatures against 24–35% elsewhere, with its cross-firm rate at the clean floor, so the signal is entirely within-firm; the contrast survives pool-size stratification and accountant-level resampling, and 262 byte-identical signatures are direct evidence of reuse. The screen thus locates where reuse concentrates and confines human review to exceptions. We report a between-accountant specificity proxy — not a within-accountant error rate, nor a bound on one — and we neither separate signing practice from a firm's imaging pipeline nor label any single signature. Calibrated on a large Chinese-signature corpus with script-agnostic descriptors, it serves as an operator-set reference point for comparable pipelines.
|
||||
|
||||
**Keywords:** signature analysis, document forensics, perceptual hashing, deep features, unsupervised calibration, audit reports, anchor-based screening.
|
||||
|
||||
## I. Introduction
|
||||
|
||||
An audit report is one of the main ways a company is held accountable to investors, and the certifying accountant's signature is the visible sign that a named professional takes responsibility for it. In Taiwan, the Certified Public Accountant Act and the attestation rules of the Financial Supervisory Commission require certifying CPAs to put their signature or seal on each audit report [1]. The law accepts either a handwritten signature or a seal, but the point of the requirement is the same in both cases: the mark on each report should stand for a deliberate, individual act of endorsement for that particular engagement [2].
|
||||
|
||||
Going digital makes that harder to guarantee. Because reports are now created, sent, and stored as electronic files, it is easy to copy an accountant's saved signature image onto many reports instead of signing each one. This can happen in two ways: a staff member can overlay a scanned signature onto the finished report (a stamping workflow), or a firm-wide electronic-signing system can do the same step automatically. We call signatures produced either way non-hand-signed. To fix the term operationally before any method is introduced: a signature is *non-hand-signed* when the mark on the report is a reproduction of a stored signature image rather than a fresh signing act for that engagement. This spans manual overlay (stamping), automated firm-wide e-signing that pastes a saved image, and proxy application of a stored image by another person. It excludes a freshly handwritten signature or a hand-applied seal made for that specific report (the in-scope "hand-signed" case), and it is distinct from a cryptographic digital signature, which binds a document mathematically rather than reproducing an image. The criterion is therefore the visible outcome — image reuse, the same stored image recurring across reports — not the intent, the actor, or the legal status, and it is this outcome that our two measures and five categories track. The worry is not about legality; it is about meaning. A single image pasted onto hundreds of reports may not carry the individual endorsement the rule assumes — a concern the literature on signatures connects to behavior, and, in auditing specifically, to rules that name and identify the engagement partner [31], [32], [33]. This is also why the problem is not forgery: a non-hand-signed signature reuses the real signer's own image, and at scale no reader can see the difference.
|
||||
|
||||
That difference matters for the method too. Almost all work on offline signature analysis is about forgery — deciding whether a questioned signature was really written by the person it claims to be [3]–[8]. In our setting the identity is not in doubt; the accountant is genuine. What we want to know is whether the person actually signed each report, or whether one signing was copied as an image. This removes the need to model clever forgers, but it adds a new difficulty: we must separate a person who signs consistently from a reused image. Someone who signs in a very steady hand will produce signatures that look alike year after year; a process that reuses one stored file will produce signatures that are structurally identical. The method has to tell these two cases apart.
|
||||
|
||||
Two facts make the obvious approach — pick a similarity cutoff and call everything above it a copy — unworkable, and they shape our design. First, archives like ours have no labels at the level of individual signatures: no signature is marked as "definitely hand-signed" or "definitely reused." Without such labels, any cutoff we choose has unknown error rates; we cannot measure how often it would wrongly flag a genuine signature or miss a reused one. Second, even setting labels aside, the data themselves do not contain a natural cutoff. As we show in Section V, the raw numbers look at first as if they split into two groups, but that appearance comes from differences between firms and from the fact that the hash takes only whole-number values; once we remove those two effects, the distribution is a single smooth spread, not two clusters. You cannot read a dividing line off a distribution that has no gap, and you cannot test a line against labels that do not exist. So the method must get its cutoff some other way.
|
||||
|
||||
Our two similarity measures are chosen precisely to expose the distinction the problem turns on. For each signature we compute two numbers against the same accountant's other signatures: a cosine similarity on deep ResNet-50 features, and an independent perceptual hash (dHash) distance. They carry different information. Cosine similarity measures overall style, and it is high both when an image is reused and when a person signs consistently. The dHash distance measures structure almost pixel by pixel, and a very small distance is the sign most specific to a reused image. But neither measure is enough on its own. Cosine alone over-flags a steady hand, because consistent signing also keeps it high. dHash alone has the opposite weakness: it is brittle to how an image is captured — a reused signature that has been re-scaled, re-cropped, or re-compressed can show a larger dHash distance and slip past a structure-only test — and a small dHash distance carries no meaning between two signatures whose styles do not match in the first place. The two are complementary precisely because they fail in different directions: cosine first establishes that the styles match, which catches reuse even when the image has been mildly altered, and dHash then asks whether the match is also near-identical in structure, which is what separates a reused image from a merely steady hand. A single similarity number blurs these two cases; two measures keep them apart. The implication between them runs one way only: a near-identical structure (a tiny dHash) forces a high cosine, but a high cosine in no way implies a near-identical structure — which is why the two-measure plane cannot be collapsed onto either single axis. This complementarity also shapes the rule (Section III-D): because a small dHash distance is only meaningful once cosine is already high, the structural cut subdivides the high-cosine cases rather than the low-cosine ones. This is the heart of the design.
|
||||
|
||||
On this basis we build and study a complete screening system. The pipeline takes raw PDF reports through four steps — find the signature page, detect each signature, turn it into features, and compute the two similarities — and sorts each signature into one of five categories. Because there is no natural cutoff to read off the data and no labels to learn one from, we instead measure how often the rule fires by chance between unrelated accountants in a clean reference group. That chance rate is a *between-accountant* coincidence rate, which we treat as a proxy for the rule's specificity: it gives us a principled way to choose an operating point, and — just as important — it tells us what each category's flag is worth among unrelated accountants. It is not the within-accountant false-positive rate (how often a genuine consistent hand-signer would fire the rule), which the reuse question would ideally use but which no labels let us estimate (Section III-E).
|
||||
|
||||
What is the screen for? Two things. Run over a large archive, it discovers where reuse concentrates — which firms, which periods — without being told where to look. And it keeps human review at the scale of exceptions. In a reuse-dominated population (a stamping firm, a firm with an electronic-signing system), the high-confidence tier routes most signatures directly to a high-specificity candidate list, and the small residual goes through a defined review protocol (specified in Section IV-B) — side-by-side overlay inspection, secondary image-artifact checks, and bounded per-accountant sampling — that also accumulates labels for later calibration. In a mixed population, where hand-signing and informal stamping coexist, the ambiguous middle is larger, and the same disposition machinery delivers the same promise one level up, at the accountant: the low-specificity advisory band is demoted rather than worked, accountant-level scores concentrate attention on the few high-ranked or mixed cases, and byte-identity hits supply proof where proof exists, confirming that an accountant's stored image is in circulation. What the screen does not deliver there — and we say so plainly when we report the category proportions (Section IV-B) — is a per-signature verdict for the ambiguous middle. In every case the output is bounded triage, not a verdict on any single signature.
|
||||
|
||||
The Taiwan setting suits this study well. The Market Observation Post System offers a large, standardized, public collection of statutory audit reports, each with the same two-signature format, which makes large-scale extraction practical. In addition, anonymized interviews with certifying partners and signing-system staff at all four firms give us institutional facts about how each firm signs and about when each firm adopted a formal electronic-signing system — adoptions that were staggered from 2020 onward. This gives the study a natural before-and-after structure in time, and outside information against which to read the firm-level results (Section III-A).
|
||||
|
||||
We make four contributions:
|
||||
|
||||
1. An end-to-end screening pipeline that turns raw audit-report PDFs into operational risk strata for hundreds of thousands of signatures.
|
||||
2. A dual descriptor that separates style consistency from image reproduction — a distinction a single similarity measure blurs.
|
||||
3. The methodological core: a label-free way to *construct and characterize* a screening operating point when no signature-level labels exist — the question this paper is really organized around. With neither a natural cutoff in the data nor labels to learn one from, we set a tunable rule by measuring how often it fires by chance in a clean reference group, and we say plainly what that measure can and cannot support. This is the part we expect to transfer beyond the present setting; we demonstrate and stress-test it at scale on audit signatures rather than claiming it as a finished, fully general framework. Concretely the result is not only a calibration method but a concrete operating point — the high-confidence rule and its measured specificity proxy — that practitioners working with comparable Chinese-signature image pipelines can use as a starting reference (not transplant unchanged, since the proxy is conditional on a similar preprocessing and reference-group setup), together with a defined disposition path for the ambiguous middle (calibrated demotion of the low-specificity band, aggregation to the accountant level, byte-identity escalation, and a bounded manual protocol) that keeps human review at the scale of exceptions.
|
||||
4. A demonstration on Chinese signatures, a structurally complex and comparatively under-served script for signature analysis. Because our descriptors work on the image rather than on script-specific strokes, the approach does not depend on Latin-script assumptions and is a candidate for other scripts.
|
||||
|
||||
The paper is organized to move from the problem to the evidence. Section II reviews related work and states the gap. Section III describes the study design — the data split, the pipeline, the five-way rule, and the calibration logic — and explains why each piece is built the way it is. Section IV reports the results: the calibration baseline, which category needs human review, and the held-out benchmark on Firm A. Section V collects supporting analyses, including the diagnostic showing that no natural cutoff exists. Section VI concludes.
|
||||
|
||||
## II. Related Work and Research Gap
|
||||
|
||||
Why reproduction matters: signatures carry symbolic weight. A signature is valuable mainly as a symbol — it stands for the signer's identity and intent. Recent experiments show that this symbolism does not survive a change in how one signs. In studies that take the reader's point of view, Chou [41] finds that electronic signatures give a weaker sense of the signer's presence than handwritten ones, and that readers therefore judge an e-signed document as less valid and expect more non-compliance; across five kinds of e-signature (a checked box, a PIN, an avatar, a typed name, and a software-generated signature), the software-generated kind felt the most "present" of the electronic options but still less than a handwritten signature. In studies that take the signer's point of view, Chou [42] finds that electronic signatures give a weaker sense of self-presence — the signer's felt attachment to the mark — and that this, in turn, makes people more willing to cheat; the work singles out signing by proxy (an autopen) as cutting the tie between the document and the signer. These results matter for us because the practice we detect — a stored signature image laid onto a report by staff or by software — is, in this scheme, one of the lowest-presence modes: it looks like a software-generated signature and is executed like a proxy signature, because the accountant performs no signing act for the report. These effects are robust rather than one-off: in a pre-registered, multi-study replication with meta-analysis, Tzelios and Williams [43] reproduce Chou's reader-side result — an avatar e-signature lowers the sense of the signer's presence and raises the expectation that the contract will be breached. In their general discussion the same authors point to accounting as a next setting — noting the spread of online tax filing and asking how digital signatures affect an evaluator's assessment of the legitimacy of claims, while cautioning that accounting documents may prove less sensitive to signature form than legal ones. We read that call precisely: their "auditors" are the readers of digitally signed filings — those who evaluate the claims — not the certifying accountants who sign. The signer-side question in auditing — what it means when the certifying professional's own signature is reproduced rather than performed — is not addressed in that literature. Both questions, reader-side and signer-side, presuppose the same missing capability: a way to measure non-hand-signing at scale. The lesson we draw is not that non-hand-signing harms audit quality — that is a separate question we leave to a companion study (Section VI) — but that whether it matters is a real question, and one nobody can study without first being able to measure non-hand-signing at scale.
|
||||
|
||||
Signature analysis to date is about forgery, not reuse. The obvious toolkit for that measurement is signature analysis, but its main concern is the wrong one for us. Bromley et al. [3] introduced the Siamese network that still anchors the field; SigNet [4] extended it to compare writers it had never seen; Kao and Wen [5] worked from a single genuine sample; TransOSV [6] brought in a Vision Transformer; and meta-learning has been used to cut the effort of enrolling new signers [16]. All of this targets imitation by another hand, so it learns to tell different people apart. Our task is the opposite: spotting reuse of the genuine signer's own image, which lives in the most-similar tail of one person's signatures. The closest idea uses reference examples to set a sensible cutoff [8], but on benchmark data with known genuine references — whereas our archive has no signature-level labels at all. This body of work is also overwhelmingly built on Western, Latin-script signatures; non-Latin scripts such as Chinese are comparatively under-served, and reported accuracies for them are lower [44]. Chinese signatures are structurally distinctive — many strokes, with wide variation between writers — and the forensic literature on them is thin; the closest precedent, Chen [45], analyzes Chinese signatures with a maximum-similarity-to-same-class statistic that directly parallels our use of the maximum cosine to the same accountant. Our descriptors, however, work on the image rather than on script-specific strokes, so the method itself does not depend on the script.
|
||||
|
||||
Image-duplication and document forensics: useful parts, different setting. A second line of work looks directly at duplicated images. Copy-move detection finds regions copied within an image [11], and Abramova and Böhme [10] adapted it to scanned documents, noting that ordinary repeated characters confuse the standard methods. Self-supervised copy detection on everyday photos [13] shows that pretrained CNN features with cosine similarity make a strong baseline for spotting near-duplicates. Closest in pipeline terms, Woodruff et al. [9] pull signatures from corporate filings for anti-money-laundering work — but to group signatures by who signed them, not to detect one signer's image being reused across documents. The building blocks exist; the specific setting — one signer's image reused across many scanned financial reports — does not seem to have been addressed.
|
||||
|
||||
Deep features and perceptual hashing as ready-made parts. Features from a pretrained CNN transfer well to document images without any retraining [20], [21], and perceptual hashes are built to survive the print–scan–rasterize cycle [27]. Jakhar and Borah [12] show that combining a perceptual hash with deep features beats either one alone for near-duplicate detection — a direct precedent for our two-measure design, though they work on natural images rather than signatures.
|
||||
|
||||
The recurring obstacle is the missing label. None of these lines solves the problem we face, because real archives carry no signature-level ground truth, and a similarity screen without it falls back on a hand-chosen cutoff whose error behavior is unknown. (The statistical tools we use to test for a natural cutoff and to describe the rule once we find none are introduced where they are used, in Section III and Section V, since they are part of our method rather than prior work on this problem.)
|
||||
|
||||
The gap, and our contribution. Two gaps follow. First, large-scale screening for non-hand-signed auditor signatures has not been done, even though there is good reason (above) to think it matters. Second, and more broadly, similarity-based screening has no principled way to set and describe an operating point when labels are missing. Our contribution sits exactly here: a label-free calibration that replaces both the arbitrary cutoff and the unavailable labeled validation with a chance-rate measured in a clean reference group, together with the pipeline and dual descriptor that make the screening possible (contributions listed in Section I).
|
||||
|
||||
It is worth being explicit about a design choice this implies, because it is easily mistaken for a missing component. A natural reflex would be to learn the discriminator — to fine-tune a Siamese or contrastive network to separate reused from hand-signed signatures. We deliberately do not, and the reason is not expedience but the defining constraint of the setting: supervised metric learning requires labeled pairs (genuine-vs-reused), which is exactly the ground truth the archive does not contain. Training such a network would require either fabricating labels or importing them from a different distribution (e.g., forgery datasets), reintroducing the unverifiable assumptions our calibration is designed to avoid; the resulting boundary would again have unknown error behavior on the real archive. Label-free operation is therefore not a weaker version of a supervised method but the only honest option when no labels exist, and the contribution is correspondingly methodological — a way to set and *characterize* an operating point by measured chance behavior — rather than a new network architecture. Off-the-shelf pretrained features are used precisely because they introduce no task-specific supervision; supervised fine-tuning is the right tool once a labeled sample exists, which is why we frame the review protocol's first run (Section IV-B, Section V) as the route to that sample and to any future supervised validation.
|
||||
|
||||
## III. Research Background and Study Design
|
||||
|
||||
This section explains how the study is built and why. We report no computed numbers here; all results appear in Section IV.
|
||||
|
||||
### A. Institutional Background
|
||||
|
||||
To pin down the signing practices that we need in order to interpret the results, we held semi-structured interviews with certifying partners and signing-system staff at all four firms in the study.¹ Three points do real work later. First, all four firms allow handwritten signing but none require it. Second, formal firm-wide electronic signing or sealing systems were adopted on staggered dates from 2020 onward. Third, one firm — which we call Firm A throughout — has used scanned-image overlay stamping as its usual practice since at least 2013. We use these facts only as background, not as labels for individual signatures: they guide how we split the data below and how we read the firm-level results in Section IV-C, but they do not tell us the status of any single signature. A further caution applies to how the interviews are used as corroboration. They are self-reported, anonymized, and not independently reproducible, so when the screen's firm-level output agrees with them (Section IV-C) that agreement is evidence of consistency with domain knowledge, not a measurement of the screen's accuracy or recall — quantifying those would require signature-level labels, which the archive does not provide. To be unambiguous about their role: the interviews are used only to *contextualize* the firm-level findings and are not treated as validation. They are corroborative, not confirmatory, and not independently reproducible; the empirical claims of this paper rest on the calibration and the byte-identical evidence, which stand without them. Their one load-bearing use is to motivate why Firm A is read as a known-positive benchmark rather than a blinded test (Section IV-C) — a framing that, if anything, lowers the evidentiary status we claim for that firm. The practical implication is that the years before the formal systems (before 2020) are the right "normal" period to use for calibration.
|
||||
|
||||
> ¹ Footnote — institutional detail. The interviews were conducted under institutional research-ethics approval and are reported in anonymized, aggregated form; firms are labeled A–D and no individual can be identified. The formal systems were reported to have been adopted at roughly one firm in early 2020, one in 2021, and one in late 2022 (exact firm-level dates are withheld for anonymity; see supplementary materials). Interviewees attributed this timing partly to the COVID-19 pandemic, which forced remote review and signing, and to firm-wide paperless and environmental (ESG) initiatives — both of which accelerated the move to formal electronic signing at Firms B/C/D. For Firm A, the reported workflow is that the certifying accountant approves the finished report electronically, after which the print room overlays the accountant's stored seal or signature image onto the PDF and prints it; the stored image is rarely changed, and although handwritten signing is allowed it is reported to be very rare, and rarer over time. Before the formal systems, the other firms' practice varied: some used informal scan- or photocopy-based stamping alongside handwritten signing, and at least one reported mostly handwritten signing before its system. The property the calibration relies on (Section III-E) is that, in the pre-2020 baseline firms, different accountants did not share a common template — not that every signature was handwritten.
|
||||
|
||||
### B. Data and Analysis Design
|
||||
|
||||
The corpus is all retrievable Taiwan statutory audit reports for fiscal years 2013–2023; the four largest firms (A–D) form the primary analysis sample, and non-Big-4 firms enter only in the crossover-scope robustness check (Section V-C), never in the calibration or the headline rates. Signatures are extracted as described in Section III-C. To be precise about the headline denominator, since it recurs throughout: "150,442 analyzable signatures" means exactly those Big-4 signatures that are valid and have both similarity measures computed, assigned by each accountant's registered firm (Firm A 60,448, Firm B 34,248, Firm C 38,613, Firm D 17,133). We then split the Big-4 corpus by firm and by period, giving each part a distinct job (Fig. 1):
|
||||
|
||||
- Calibration (the clean reference group): Firms B/C/D, 2013–2019.
|
||||
- Held-out benchmark 1: Firm A, 2013–2023 (a known positive, not a blinded test).
|
||||
- Held-out test 2 (secondary): Firms B/C/D, 2020–2023.
|
||||
|
||||
We explain the reason for each part in Section III-E. The key idea is simple: we calibrate only on the clean cell — the non-Firm-A firms in the years before formal systems — and test everything else against it. No numbers appear here; the calibration results start in Section IV-A.
|
||||
|
||||

|
||||
|
||||
*Figure 1. The data split. Rows are Firms A–D; columns are 2013–2019 and 2020–2023. The B/C/D × 2013–2019 cells are the clean calibration group; Firm A (both periods) is held-out benchmark 1 (a known positive); B/C/D × 2020–2023 is the secondary held-out test. We calibrate only on the clean cell and test everything else against it.*
|
||||
|
||||
### C. Pipeline
|
||||
|
||||
The pipeline turns a raw PDF report into labeled signatures in five steps (Fig. 2).
|
||||
|
||||
Finding the signature page. A vision-language model [24], [35] scans only the first quarter of each document — where the auditor's report page reliably sits — and stops as soon as it finds the page.
|
||||
|
||||
Detecting signatures. A YOLOv11n detector [25], [34], trained on 500 hand-labeled signature pages (425 for training, 75 for validation; 100 epochs; started from COCO weights), draws a box around each signature. A region counts as a signature if it holds handwritten content that belongs to a personal signature, even where it overlaps an official stamp. A red-stamp removal step (filtering in HSV color space) then strips away overlapping red seals, leaving the handwritten part.
|
||||
|
||||
Turning signatures into features. Each detected signature is passed through an ImageNet-pretrained ResNet-50 [26] used as a fixed feature extractor — we take the 2,048-number output of its global-average-pooling layer and drop the classification head. We resize each image to 224×224 while keeping its aspect ratio (padding with white), apply the standard ImageNet normalization, and scale the feature vector to unit length, so that cosine similarity is just the dot product. We use these off-the-shelf features rather than fine-tuning the network, for three reasons: the task is comparing similarity, not classifying; ImageNet features are known to transfer well to document images [20], [21]; and not fine-tuning avoids the risk of learning quirks of our particular dataset. The backbone choice is checked in Section V-C.
|
||||
|
||||
Assigning each signature to an accountant. Each signature is matched to a registered accountant by its position on the page (first or second) against the official registry. Signatures we cannot match are left out of the same-accountant comparisons, because the "most similar signature by the same accountant" measure has no meaning without an assigned accountant.
|
||||
|
||||
(Detection accuracy, signature counts, match rates, and the resulting analysis sample are reported in Section IV-A.)
|
||||
|
||||

|
||||
|
||||
*Figure 2. The screening pipeline. A raw PDF passes through page-finding (a vision-language model), signature detection (YOLOv11) with red-stamp removal, feature extraction (ResNet-50), the two per-signature similarities (cosine for style; the smallest dHash to the same accountant for structure), and a five-way label.*
|
||||
|
||||
### D. The Two Similarity Measures and the Five-Way Rule
|
||||
|
||||
For each signature we compute two numbers, both against the same accountant's other signatures: cos, its highest cosine similarity to another of that accountant's signatures, and dHash, its smallest perceptual-hash distance to another of them. As explained in Section I, the point of using two measures is to separate two things that one measure blurs. A high cos means the signatures look alike in style, which happens both when an image is reused and when a person signs consistently. A small dHash means the signatures are alike almost pixel for pixel, which is the sign most specific to a reused image. Together they are far more telling than either alone: a steady hand gives a high cos but a dHash that still varies, while a reused image gives a high cos and a tiny dHash.
|
||||
|
||||
The rule places each signature in one of five categories, with cosine acting as the primary gate and the structural (dHash) distance refining only the cases where cosine is already high. Each name states the screening hypothesis its region suggests — a candidate reading, not a confirmed determination:
|
||||
|
||||
- HC — high-confidence reuse candidate: cosine above the high cut and structure at or below the near-identical cut. Both measures point to a reused image.
|
||||
- MC — moderate-confidence, advisory: cosine above the high cut and structure between the two structural cuts. Style is very similar, but structure is below the strict bar.
|
||||
- HSC — high style-consistency: cosine above the high cut and structure above the upper structural cut. Style is similar with no structural support.
|
||||
- UN — uncertain: cosine between the low cut (the same-vs-different-accountant crossover) and the high cut.
|
||||
- LH — low reuse-similarity: cosine at or below the low cut.
|
||||
|
||||
A report takes the strongest label among its signatures (HC > MC > HSC > UN > LH). Table I-a summarizes the five categories, the thresholds that define them, and the notation used throughout; the high cut is cosine 0.95, the low cut is 0.8547 (the same-vs-different-accountant crossover; Section IV-A), and the two structural cuts are dHash 5 and 15.
|
||||
|
||||
**Table I-a — Category definitions, thresholds, and notation.**
|
||||
|
||||
| Label | Name | Condition (cosine *c*, structure dHash *d*) | Role |
|
||||
|---|---|---|---|
|
||||
| HC | high-confidence reuse candidate | *c* > 0.95 and *d* ≤ 5 | self-certifying flag |
|
||||
| MC | moderate-confidence | *c* > 0.95 and 5 < *d* ≤ 15 | advisory |
|
||||
| HSC | high style-consistency | *c* > 0.95 and *d* > 15 | no structural support; no weight |
|
||||
| UN | uncertain | 0.8547 < *c* ≤ 0.95 | ambiguous middle |
|
||||
| LH | low reuse-similarity | *c* ≤ 0.8547 | likely hand-signed |
|
||||
|
||||
Other abbreviations used throughout: ICCR — inter-CPA coincidence rate, the between-accountant chance-firing rate that calibrates the rule (Section III-E); *c* — cosine similarity to the same accountant's other signatures (style); *d* — smallest dHash distance (structure).
|
||||
|
||||
Why the partition has this shape (five categories, not nine). As explained in Section I, a near-identical structure is decision-relevant only once the styles already match, so the two cosine cuts come first — splitting signatures into three style bands (low, uncertain, high) — and the two structural cuts subdivide only the high band. Three facts pin this shape down. First, structure carries little standalone decision weight in the two lower bands: between signatures whose styles do not clearly match, a moderate structural distance is hash noise, not evidence of reproduction — and even the near-identical structural matches that do appear below the style cut (quantified next) are not assigned HC; their structural information re-enters only through accountant-level aggregation and byte-identity review (Section IV-B), not through a separate cell. Second, the cells of the full 3×3 grid that pair a lower style band with a near-identical structure are sparsely populated rather than ignored — and the empirical reading is more precise than a simple "they are empty." An explicit count makes this exact: of the 150,442 Big-4 signatures, 7,681 (5.1%) combine a near-identical structural match (dHash ≤ 5) with a sub-0.95 cosine, so the one-way implication of Section I (a tiny dHash forces a high cosine) holds approximately, not strictly. But the residents' mass sits immediately below the high-cosine cut — 7,311 of them (95.2%) fall in cosine 0.90–0.95, and only 370 signatures (0.25% of the corpus) reach the genuinely low-cosine bands, of which just 38 lie below the LH/UN crossover (cosine ≤ 0.8547). These residents are not degenerate crops: their image size (mean 33k px) and detection confidence (0.875) match the rest of the corpus (28k px, 0.877). Under the coherent same-pair definition — style and structure satisfied on the same partner signature — the count falls further to 874 (0.58%). The point is therefore not that these cells are empty but that subdividing the lower style bands by structure changes no disposition: because cosine is the primary gate, a near-identical structural match beneath the style cut is already handled as UN, and the residual structural information re-enters through the accountant-level aggregation and byte-identity escalation of Section IV-B rather than through a separate cell. Third, a partition should cut only where the resulting actions differ: subdividing the two lower bands by structure would create cells whose dispositions (Section IV-B) are identical — all demoted or aggregated the same way — adding calibration burden without operational consequence, whereas the three structural cells inside the high band exist precisely because their dispositions differ. (Count from the deployed-rule descriptor columns; any-pair definition, full Big-4 corpus.)
|
||||
|
||||
The cuts are operator-tunable operating points, not learned boundaries: there is no natural gap to read off the data (Section V-A) and no signature-level labels to learn one from, so the cuts are chosen and their specificity is measured, not learned. The four cut values, and where each one comes from — two are read directly from this study's data — are given in Section IV-A, alongside the chance-rate calibration that characterizes them and the figure of the two-measure plane (Fig. 3).
|
||||
|
||||
Any-pair versus same-pair: how the two extrema combine. One construction detail deserves to be explicit, because a careful reader will ask. The two per-signature values are independent extrema over the same accountant's other signatures — the highest cosine and the smallest dHash, each taken on its own — so the two values may come from different partner signatures. We call this the any-pair rule, and the choice is deliberate, for three reasons. First, the two descriptors have different invariances: cosine survives re-scaling and re-compression; dHash does not. For a genuinely reused image that crossed different scan or compression pipelines, the style-nearest copy and the pixel-nearest copy can therefore legitimately be different reports — forcing both extrema onto one pair would miss exactly that most realistic positive case. Second, dHash takes whole-number values and ties are massive in duplicate-heavy pools: which tied copy wins the minimum is essentially arbitrary, so whether the two extrema land on the same file is largely tie-breaking noise — both point into the same duplicate cluster. Third, the chance-rate calibration of Section IV-A applies the same any-pair rule to the clean reference group, so the high-specificity claim rests on the absolute clean-group rate (the HC rule fires by chance on only ~1.2% of clean-group reports), not on any firm-versus-floor ratio; the same rule is applied to every firm and to the reference group alike. The stricter same-pair variant, in which a single partner signature must satisfy both inequalities at once, is reported as a robustness check (Section V-C) and leaves every conclusion unchanged — the within-firm concentration of cross-accountant matches is in fact *higher* under same-pair (97.0–99.96% across the four firms) than under the deployed any-pair rule (76.7–98.8%) — because in the high-confidence region the two rules nearly coincide: a partner within the near-identical structural cut is pixel-near-identical and therefore clears the high style cut by itself.
|
||||
|
||||
Limitations, stated up front. Three follow directly from the design. (i) Because the cutoffs are chosen rather than learned, the system has a tunable operating point, not an optimal one. The dial moves in only one direction: a reviewer who wants more conservatism can tighten it for higher specificity, but cannot trade in the other direction toward higher recall, because recall is unobservable without labels — there is no precision–recall frontier to navigate (Section V). (ii) The chance rate we report is a *between-accountant* coincidence rate — a proxy for specificity, not a true false-acceptance rate — because we have no labeled negatives; it speaks to how often unrelated accountants collide, not to how often a genuinely consistent hand-signer would fire the rule, which is the quantity the reuse question needs and which we cannot estimate (Section III-E). (iii) For any single signature, the two measures cannot tell us why it is so similar to another: reuse of an image, a shared scanning pipeline, and a very uniform house style all push the numbers the same way, and we do not try to choose between them at the level of one signature. These limits apply to every claim that follows.
|
||||
|
||||
### E. The Clean Reference Group and the Chance Rate
|
||||
|
||||
With no labeled negatives to learn from, the calibration uses a stand-in: a group in which the rule should fire only by chance — unrelated accountants whose signatures happen to look alike now and then. Choosing this group well is the central design decision, and two requirements force the choice.
|
||||
|
||||
Why not all four firms. As Section IV-C will show, almost all of one firm's between-accountant matches fall on other accountants of the same firm, and we have byte-level proof of image reuse across about fifty of that firm's partners. If we put Firm A into the reference group, we would be filling the "by chance" rate with exactly the within-firm matches the rule is supposed to catch — a circular calibration. So we use Firms B/C/D as the clean reference group and keep Firm A as a test case; we report the all-four-firm number only to show how much Firm A contaminates it.
|
||||
|
||||
Why 2013–2019. We further limit the reference group to the years before formal firm-wide electronic-signing systems (adopted from 2020 onward; Section III-A). What this buys us is the absence of a shared template across accountants — not a guarantee that every signature was handwritten. The interviews say some baseline firms used informal individual stamping before 2020, but each accountant's stored image was their own, so different accountants' signatures still match only by chance; the chance rate is about matches between accountants, which individual stamping does not inflate. One further channel deserves to be named, because it is not the template and we cannot fully exclude it: accountants at the same firm pass through a shared imaging pipeline — common scanners, PDF-assembly software, and the red-stamp-removal step (Section III-C, Section V-B) — and a shared pipeline can imprint correlated artifacts on otherwise-unrelated signatures, which would lift the inter-CPA rate above true chance. The pipeline audit of Section V-B confirms that such shared production paths exist and change over time. This is a reason to read the ICCR as a *specificity proxy* rather than a literal coincidence rate; its bias, like reference contamination, runs toward a higher floor, which makes the Firm-A contrast more conservative rather than less. After 2020, formal systems standardize how reports are assembled, so that period is not a clean reference — and indeed the chance rate rises after 2020 (Section V-B). We therefore calibrate on the Firms-B/C/D 2013–2019 cell and score every held-out cell against it.
|
||||
|
||||
We report the rule's chance rate at three levels, because the rule takes the best match over a pool and so the per-signature rate is not the same as the per-pair rate: per comparison (sampled pairs of different accountants), per signature, and per report, each with a confidence interval. We call this the inter-CPA coincidence rate (ICCR) rather than a "false-acceptance rate," which we reserve for settings that have labeled negatives. The ICCR is a *between-accountant* coincidence rate: how often the rule fires on the signatures of two *different* accountants. It is therefore at best a proxy for specificity, and only under the stated assumption (no shared template across accountants). It is important to be exact about what it is not. The quantity the reuse question actually needs is the *within-accountant* false-positive rate — how often the rule would fire on a genuinely consistent hand-signer's own signatures — and that rate is not estimable here, because no accountant in the corpus is labeled as a known hand-signer. We considered benchmarking it against an external corpus of genuine repeated signatures (a public signature dataset supplies many authentic samples per writer), but such corpora are a different population and script acquired under a different pipeline, so the resulting rate would not transfer to this setting; importing it would reintroduce exactly the kind of unverifiable cross-distribution assumption our label-free calibration is built to avoid. We therefore report the limitation rather than a misleading proxy. The ICCR is not even a bound on it: a uniform individual hand keeps cosine high by design, so a true hand-signer's within-accountant fire rate can sit far *above* the between-accountant coincidence rate. Any statement that divides a firm's within-accountant fire rate by this between-accountant floor (an "X× the floor" comparison) therefore overstates the gap — the bias runs in the anti-conservative direction — and we do not report such ratios as effect sizes. Read as a between-accountant specificity proxy under the stated assumption, the ICCR is faithful to the evidence; read as a true error rate for the reuse question, it would claim more than we can show.
|
||||
|
||||
One further assumption deserves to be stated rather than buried, because it concerns how the clean group was chosen. The floor is *conditional on the reference group actually being clean* — it is a coincidence rate among accountants we take to be independent hand-signers, and the group (non-Firm-A firms, pre-2020) was selected partly because its rates are low and its practices, by the interviews, are not stamping-dominated. That selection is mild but not innocent: if some baseline accountants in fact reuse images undetected, the reference is contaminated. The direction of that error, however, is reassuring for the Firm-A contrast. Undetected reuse inside the baseline would only *raise* the between-accountant coincidence floor, which makes Firm A's gap above it *smaller*, not larger — so contamination of the clean group biases the headline contrast conservatively, against our conclusion rather than toward it. Two pieces of evidence bound the concern empirically. First, the three baseline firms are mutually consistent and uniformly low (Firms B and C within about 3.5× of each other, none close to Firm A; Section IV-A), so the floor does not hinge on any single firm and a leave-one-baseline-firm-out reading does not move it materially. Second, the one data-derived threshold, the low cosine cut, is stable when the group composition is changed — 0.8547 on the calibration cell, 0.8302 with the non-Big-4 firms folded in, a shift of at most 0.025 (Section V-C) — so widening or narrowing the reference at its boundary does not move the operating point. We therefore treat the clean-group assumption as a stated limitation with a known-safe error direction, not as a hidden premise.
|
||||
|
||||
### F. What HC Means and Does Not Mean
|
||||
|
||||
One sentence prevents the most common misreading of everything that follows. *HC is not a reuse label.* HC denotes an extreme *within-accountant repetition pattern* — a signature whose closest match among the same accountant's own signatures is both stylistically near-identical (cosine > 0.95) and structurally near-identical (dHash ≤ 5) — that is *statistically rare between unrelated accountants*, by the ICCR calibration of Section III-E. That is the whole of what the rule, on its own, establishes. Reuse of a stored image is one interpretation of an HC pattern, and the most economical one, but the rule does not imply it: a very steady hand, a fixed scanning-and-assembly pipeline, or a uniform house style can each raise within-accountant repetition (Section V-A, Section V-B). Where we go further than "extreme repetition" — as we do for Firm A — the additional weight comes from outside the rule: byte-identical signatures, which independent hand-signing cannot produce, and the institutional context, neither of which is implied by HC alone. For Firms B/C/D we make no reuse claim at all; their HC signatures are reported as a within-accountant repetition rate, not as detected reuse. Read this way, HC is a calibrated, reproducible screening category, and "reuse" is a conclusion that has to be earned separately — firm by firm, or signature by signature — rather than read off the label.
|
||||
|
||||
## IV. Findings
|
||||
|
||||
This section reports the numbers. It starts with the calibration baseline (Firms B/C/D, 2013–2019), then says which category needs human review, then presents the held-out benchmark on Firm A.
|
||||
|
||||
### A. Detection Sample (Whole Corpus) and the Calibration Baseline (Firms B/C/D, 2013–2019)
|
||||
|
||||
Detection and the analysis sample (whole corpus). Two scopes appear in this section and must not be confused: detection and the analysis sample here are computed on the whole corpus, whereas both data-derived calibration quantities — the chance-rate ICCR and the low cosine cut (Section IV-C) — are computed only on the clean Firms-B/C/D 2013–2019 cell. Of the 90,282 reports, the page-finder flagged 86,084 as having a signature page (the other 4,198, or 4.6%, had none); 13 of those 86,084 could not be rendered, leaving 86,071 documents processed. On the validation set, the YOLOv11n detector reached precision 0.97–0.98, recall 0.95–0.98, mAP@0.50 0.98–0.99, and mAP@0.50:0.95 0.85–0.90. Across the corpus it extracted 182,328 signatures — 2.14 per document with detections, where two certifying accountants per report implies 2.00. The ≈6.7% excess is explained by extra detections rather than missed accountants: of the 13,573 detections (7.4%) that could not be matched to a registered accountant and were excluded, 8,901 (66%) are third-or-later detections on a page — boxes beyond the two certifying signatures — and the unmatched set as a whole carries lower detection confidence than the matched set (mean 0.826 vs 0.874), consistent with these being extra boxes and low-confidence noise; the remaining 4,672 are first/second-position detections that failed registry matching. Throughput was 43.1 documents per second, and the detector agreed with the vision-language model on 98.8% of documents. Matching by position assigned 92.6% of signatures (168,755 of 182,328) to a registered accountant; of these, 168,740 have both similarity measures computed (the 15-signature difference is accountants with a single signature in the corpus, for whom no same-accountant comparison exists, so the full-corpus distributional statistics in the Appendix are reported on 168,740). The four-firm analysis sample is 150,442 signatures with both measures computed, from 457 accountants (Table I); the accountant-level partition (Table III, Section V-C) is fit on the 437 of these with at least ten signatures (171/112/102/52 across Firms A–D).
|
||||
|
||||
**Table I — Detection and extraction summary.**
|
||||
|
||||
| Quantity | Value |
|
||||
|---|---|
|
||||
| Documents with a signature page | 86,071 |
|
||||
| Detector precision / recall | 0.97–0.98 / 0.95–0.98 |
|
||||
| Detector mAP@0.50 / mAP@0.50:0.95 | 0.98–0.99 / 0.85–0.90 |
|
||||
| Signatures extracted | 182,328 (2.14 per document) |
|
||||
| VLM–detector agreement | 98.8% |
|
||||
| Signatures matched to an accountant | 168,755 (92.6%) |
|
||||
| Four-firm analysis sample | 457 accountants; 150,442 signatures |
|
||||
|
||||
The calibrated operating point: the four cut values and their bases. The five-way rule of Section III-D uses four cut values; we state them here because two are read directly from this study's data. The low cosine cut, 0.8547, is the crossover of the same-accountant and different-accountant cosine distributions computed on the calibration cell alone (Firms B/C/D, 2013–2019, closed-world: both the source signatures and their comparison set drawn from that cell; Section IV-C). We use this closed-world value as the primary cut rather than the corpus-wide crossover, so that the one data-derived threshold in the rule is estimated only on the calibration-only Firms-B/C/D 2013–2019 cell, held out from Firm A and from post-2020 scoring. The cut is stable across scopes — 0.8547 (calibration closed-world), 0.8367 corpus-wide, 0.8489 on the all-period baseline firms, 0.8302 with the non-Big-4 firms added; it moves by at most 0.025 across all four scopes (0.018 from the corpus-wide value), so the choice of scope is immaterial and the broader-scope values stand as robustness checks (Section V-C). The high cosine cut, 0.95, is the high-similarity operating point: it sits in the region where genuine reuse concentrates — the byte-identical anchor (Section IV-C) lies at cosine 1 — and a recalibration cannot move it onto a distributional antimode because none exists (no within-population bimodality, Section V-A). The near-identical structural cut, dHash ≤ 5, is the perceptual-hash distance below which two rasters are pixel-equivalent up to mild recompression, and dHash ≤ 15 bounds the looser "structurally similar" band; both follow the standard 64-bit dHash distance scale [27]. We therefore do not re-derive these three as optimal cutoffs but characterize their chance-of-firing behavior directly (the full prior-calibration provenance is in the supplementary materials), and we make them operator-tunable in one direction: their specificity proxy at these values is read off the chance-rate calibration below, and an operator can tighten the floor by inverting the ICCR curve (for example, dHash ≤ 3). This is a conservativeness dial, not a precision–recall control: tightening raises the specificity proxy and lowers the flag count, but there is no observable recall to trade back, so loosening cannot be calibrated against a known cost. We deliver these as a concrete, calibrated operating point — in particular the high-confidence (HC) rule, cosine > 0.95 and dHash ≤ 5 — whose between-accountant coincidence behavior the calibration below makes explicit. Because the rule is calibrated on a large Chinese-signature corpus, the HC values double as a practical starting reference for practitioners working with comparable Chinese-signature image pipelines, rather than a setting to transplant unchanged.
|
||||
|
||||

|
||||
|
||||
*Figure 3. The two measures and the five regions, drawn as the real 2D density of all Big-4 signatures (n = 150,442; log color scale, integer dHash bins). The cosine axis is split at the low cut 0.8547 (the calibration-cell same-vs-different-accountant crossover) and the high cut 0.95; within the high-cosine band the dHash axis is split at 5 and 15. The mass concentrates in the bottom-right HC corner — high cosine with near-identical structure — and thins out as a single continuum toward lower cosine and higher dHash, with no gap separating a "reuse" cluster from a "hand-signed" one (Section V-A); note also that essentially all signatures sit above cosine ≈ 0.85, the compressed high-similarity range discussed in Section V-A.*
|
||||
|
||||
The calibration sample itself (Firms B/C/D, 2013–2019). The chance-rate calibration that follows is computed on the clean cell only, and the reader should be able to see the calibration base directly rather than infer it from the full-period totals above. The Firms-B/C/D 2013–2019 cell contains 226 accountants, 52,071 signatures with both measures computed, and 26,042 reports; the per-comparison ICCR below is estimated from 5×10⁵ inter-CPA signature pairs sampled uniformly from this cell. Every ICCR source signature is restricted to this cell — the headline per-signature and per-document rates reproduce on the 52,071-signature 2013–2019 cell, not on the full-period BCD record (~90,000 signatures), which is used only where a robustness figure is explicitly quoted — so no post-2020 or Firm-A signature enters the calibration.
|
||||
|
||||
How often the strict rule fires by chance (pooled). In the Firms-B/C/D 2013–2019 group, the strict (HC) rule fires by chance very rarely at every level (Table II): about 1 in 100,000 per comparison (Wilson 95% CI [4×10⁻⁶, 2.3×10⁻⁵]), 0.59% per signature ([0.45%, 0.73%]), and 1.2% per report. These are roughly ten times lower than the contaminated all-four-firm figures (1.4×10⁻⁴, 11.0%, 18.0%); the difference is exactly the within-firm matching that the clean group leaves out. So a clean group of unrelated accountants almost never produces an HC report, which makes HC a high-specificity operating point. (The per-comparison figure rests on a small number of chance hits — 5 of 5×10⁵ pairs — and is best read as an order-of-magnitude value; the per-signature and per-report figures, which are well powered, carry the weight.) A low rate is not a small number at archive scale, and we state the absolute consequence plainly: applied blindly across all 150,442 analyzable Big-4 signatures, the clean per-signature rate alone would be expected to yield about 888 HC flags by chance (95% CI [677, 1,098]), scaling further if the screen is run over the full archive. This is exactly why a single HC flag is never read in isolation: the evidential weight is carried by the firm-level contrast (Section IV-C) and accountant-level aggregation (Section IV-B), not by a raw archive-wide HC count.
|
||||
|
||||
**Table II — Chance-firing rates (ICCR) by level and group: the strict HC rule (top two rows), with the looser MC band's per-report rate shown for contrast (bottom row).**
|
||||
|
||||
| Group / rule | Per comparison | Per signature | Per report |
|
||||
|---|---|---|---|
|
||||
| HC — B/C/D 2013–2019 (calibration) | 1.0×10⁻⁵ [4×10⁻⁶, 2.3×10⁻⁵] | 0.59% [0.45%, 0.73%] | 1.2% |
|
||||
| HC — all four firms (contaminated) | 1.4×10⁻⁴ | 11.0% | 18.0% |
|
||||
| MC band (HC+MC) — B/C/D 2013–2019 | — | — | ≈17.5% |
|
||||
|
||||
Each baseline firm on its own (B, C, D). Reported separately, the three baseline firms are alike and uniformly low. A logistic regression of the per-signature HC flag on firm (with Firm D as the reference) over the baseline cell puts Firms B and C within about 3.5× of each other (odds ratios 1.73 and 0.49), and none of them comes close to the high rates we see for Firm A in Section IV-C. The 2013–2019 five-way breakdown for each of Firms B/C/D (counts and within-firm percentages) is reported in Table II-b; the full-period (2013–2023) breakdown is in Table IV for reference.
|
||||
|
||||
**Table II-b — Five-way breakdown for each baseline firm, calibration period (B/C/D, 2013–2019).**
|
||||
|
||||
| Firm | HC | MC | HSC | UN | LH | signatures |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Firm B | 29.04% | 39.31% | 0.39% | 30.91% | 0.35% | 19,677 |
|
||||
| Firm C | 21.59% | 42.09% | 0.37% | 35.53% | 0.43% | 22,449 |
|
||||
| Firm D | 22.01% | 29.67% | 0.20% | 47.35% | 0.76% | 9,945 |
|
||||
|
||||
One point in Table II-b needs to be made explicit, because at first glance it looks like a contradiction: the within-firm HC percentages here (Firm B 29.0%, Firm C 21.6%, Firm D 22.0%) are an order of magnitude above the 0.59% chance rate of Table II, even though both are computed on the same clean calibration cell. They are not in tension, because they measure different things. The 0.59% is a *between-accountant* rate — how often the HC rule fires on the signatures of two *different* accountants — and that is the quantity the calibration uses and that stays tiny. The 21–29% figures are *within-accountant* rates — how often an accountant's own signatures fire the rule against each other — and a substantial within-accountant rate is exactly what one expects from anyone with a consistent hand or a uniform house style, before any reuse is invoked; at these firms it also carries a genuine but smaller component of image reuse that grows after 2020 (Section V-B). "Clean," in this paper, means the between-accountant coincidence is rare, not that within-accountant similarity is low. This is also why the within-accountant rate cannot be read as a false-positive rate (Section III-E), and why the contrast that isolates Firm A in Section IV-C is not "HC fires at all" but that Firm A's within-accountant rate (82%) stands far above the 21–29% of three otherwise-alike firms.
|
||||
|
||||
### B. From Categories to Actions: Review as Exception Management
|
||||
|
||||
The proportions first, stated plainly. Before saying what to do with each category, we show how much of the data falls into each (Table IV, full corpus): HC 49.6%, MC 26.5%, UN 23.4%, HSC 0.2%, LH 0.3%. The ambiguous middle (MC + UN) is therefore not a fringe: about half of all four-firm signatures, and 65–76% at Firms B/C/D individually, against 18.1% at Firm A. Read against the institutional background (Section III-A), this is exactly the expected shape. Firm A behaves in the screen as a reuse-dominated population — a reading consistent with the interviews and with the byte-identical evidence, though it rests on those rather than on per-signature ground truth — and the screen settles most of its signatures outright; Firms B/C/D in this period are mixed populations in which hand-signing and informal individual stamping coexist, so per-signature similarity is genuinely ambiguous there. The right response to a large middle is not to hide it but to give it a disposition path that does not require a per-signature verdict. Four moves do this. To be clear about what is established versus proposed here: the category proportions above, the per-band chance rates, and the byte-identity counts are empirical results, whereas the four moves are a *designed* operating procedure derived from the calibration — they are an argument that the workload is tractable, not a validated workflow. The protocol's end-to-end first run on a bounded, human-labeled sample, which is what would actually measure its discriminating behavior, is left to future work (Section V); we therefore present the moves as the intended use of the calibrated rule, not as evidence in their own right.
|
||||
|
||||
Move 1 — calibrate each band's evidential weight, and demote what fails. The calibration tells us what each flag is worth. The HC band fires by chance on only about 1.2% of reports in the clean reference group, so an HC flag is close to self-certifying: it needs essentially no verification effort, and it goes straight onto the action list — findings to count, report, or investigate — rather than onto a list of flags still to be checked. The MC band fires by chance on about 17.5% of reports in the clean reference group — roughly one clean-group report in six — and, unlike HC, this rate does not drop when Firm A's accountants are excluded from the cross-accountant comparison pool (it edges up, because removing Firm A's distinctive template leaves a pool whose members resemble one another a little more at the coarse dHash ≤ 15 scale); the boundary at dHash = 15 also sits in a flat region of the sensitivity sweep, adding flagged cases without adding specificity (Section V-C). An MC flag on its own therefore carries almost no information and does not justify verification effort; it matters only in combination with other evidence. The UN band is ambiguous in the same spirit and is treated alongside MC; on the clean baseline the UN cosine band is reached by chance about 88% of the time per signature (98.2% per report), confirming that a UN flag is essentially uninformative about reuse on its own, whereas the HSC band is reached by chance only about 0.13% of the time per signature (0.25% per report) and in any case points away from reuse (style match without structural support). The HSC band is tiny (0.2%), so it warrants only a light spot-check. The LH band needs no action. Demotion, however, only says what an MC or UN flag is not — standalone evidence; what becomes of these signatures is the business of the next three moves: their information flows into the accountant-level scores (Move 2), which byte-identity hits then sharpen by proving that an accountant's stored image is in circulation (Move 3); the residual's data needs are named rather than guessed at (Move 4); and where a human does look at individual cases, the bounded protocol specified below applies.
|
||||
|
||||
Move 2 — lift the unit of decision from the signature to the accountant. The middle categories rarely need to be resolved one signature at a time, because the operational question is almost always about an accountant or a firm, and the ambiguous signatures still carry information at that level. Three accountant-level scores — a mixture-model position score on the two-measure plane, a percentile relative to an external non-Big-4 reference population, and the accountant's own rate of replication-consistent labels — rank the 437 accountants in close agreement (Spearman ρ ≥ 0.879; reported as internal consistency among scores built on the same descriptors, not as external validation). A signature that is individually undecidable still moves its accountant's position; several hundred per-signature questions collapse into one per-accountant judgment.
|
||||
|
||||
Move 3 — anchor with byte-identity, the one check that yields certainty. An exact byte-level comparison costs little, and what it finds is proof rather than evidence: independent hand-signing cannot produce byte-identical images, so every byte-identical pair is confirmed reuse with no human judgment required (the corpus contains 262 such signatures; Section IV-C). To be precise about where this bites: a byte-identical pair has cosine 1 and dHash 0, so these signatures sit in HC by construction — byte-identity rescues no case from the ambiguous middle. Its role is twofold. Within HC, it upgrades a subset from high-confidence candidate to logical certainty, removing even the pipeline-and-house-style caveat of Section III-D for those cases — the difference between a statistical screen and an exhibit one can act on without qualification. And at the accountant level, a byte-identical hit proves that a stored image of that accountant is in circulation, which raises the prior on the rest of that accountant's near-identical cluster — including its MC and UN members — and thereby sharpens the per-accountant judgment of Move 2. (A byte-identical pair has cosine = 1 and dHash = 0, so it falls in HC by construction; that the rule "captures" the whole byte-identical set is therefore tautological, and we do not read it as a recall measure.)
|
||||
|
||||
Move 4 — state what the residual needs, instead of classifying it anyway. After the three moves, a residual middle remains whose mechanism the two measures genuinely cannot identify: reuse through a noisy pipeline, a very steady hand, and a homogeneous scanning infrastructure can occupy the same spot on the plane. We name the data that would resolve it — a proposed resolution path, not one executed in this study. Image-acquisition metadata is machine-readable provenance that could be extracted automatically rather than judged by eye: scanner identifiers and PDF-generator strings recorded in the files themselves, and compression markers such as JPEG quantization tables, which encode the processing history an image has been through. This adds the axis the two similarity measures lack — two near-identical images that arrived through different production pipelines are hard to explain except by reuse, while two that shared one pipeline may owe their similarity to the pipeline itself. (Whether this provenance survives the upload platform is itself an empirical question, and we checked: we verified across a stratified sample of MOPS reports (all four firms, 2014–2022) that producer/creator strings, PDF versions, and image encodings are heterogeneous report-to-report — distinct scanner models (Fuji Xerox D125, ApeosPort-III/IV/V), born-digital producers (Microsoft Word, Adobe, Acrobat Distiller), and a mix of CCITT-grayscale and JPEG-RGB encodings at differing resolutions — so the platform does not flatten uploads to a uniform template and the acquisition history is recoverable here; firms' own internal archives would retain at least as much.) A small labeled set of known hand-signed examples — certified by the firms, or accumulated case by case as a by-product of the review protocol below — would turn the chance-rate calibration into directly estimated error rates. Naming these is the honest alternative to pretending the residual can be classified from similarity alone.
|
||||
|
||||
Where a human does look, the review follows a defined and bounded protocol. We specify the protocol here as a design deliverable of the method: the discriminating behaviors stated below are design expectations, following from the artifact properties of reused versus independently signed images, and the protocol's first execution, on a bounded sample, is listed as future work (Section VI). (1) Side-by-side overlay inspection: the reviewer is shown the flagged signature next to the same-accountant signature(s) that produced its score, with a pixel-difference overlay and an edge-aligned superposition; a reused image is expected to overlay almost exactly, whereas two independent signings show natural variation in pressure, ink, and baseline. (2) Secondary artifact checks not used by the rule — exact registration, JPEG and scan-noise fingerprints (the compression and anti-aliasing traces a reused raster carries with it), and scaling traces — are designed to separate a reused raster from a re-scanned genuine signature at low cost. (3) Document and time context: the reviewer checks whether the matched signatures come from reports of different dates or engagements (reuse across time is more telling than within a single filing) and whether the surrounding layout shows a standard template or stamp. (4) Bounded per-accountant sampling: because the operational question is usually at the accountant or firm level, the reviewer judges a bounded random sample per accountant rather than every flagged signature, keeping the effort proportional to the number of accountants, not the number of signatures. (5) Feedback into calibration: each adjudicated case yields a label — reuse, hand-signed, or undetermined — and these accumulate into the small ground-truth set the setting otherwise lacks, which can later tighten the operating point or support supervised validation. The protocol's relation to Move 4 is one of scale: steps 1–3 apply per-case versions of the same artifact evidence that Move 4 would collect corpus-wide, step 4 bounds how many cases a human ever sees, and step 5 accumulates the labeled set Move 4 asks for. What the protocol cannot do — and is not claimed to do — is resolve the residual at scale; that is exactly what the corpus-wide metadata collection of Move 4 would add.
|
||||
|
||||
Why this is exception management rather than caseload. Where a firm's output is dominated by reuse, the high-confidence tier settles most signatures directly (82% at Firm A), and the four moves reduce the remaining 18% to per-accountant judgments and a review queue bounded by the number of accountants rather than the number of signatures — exception management at the signature level. In a mixed population the same machinery delivers the same promise one level up, at the accountant. Move 1 removes the bulk of the apparent caseload outright: an MC flag alone does not justify verification, which at Firms B/C/D takes 29–41% of signatures off the worklist before anyone is assigned to anything (the UN band carries no flag in the first place, so the demotion bites on MC alone). Move 2 positions every accountant on the replication-dominance spectrum, so attention concentrates on the few high-ranked or mixed cases rather than on tens of thousands of signatures. Move 3 supplies proof where proof exists: 117 of the 262 byte-identical signatures sit at Firms B/C/D, demonstrating that stored-image reuse is a real practice at the mixed firms too, and anchoring the accountant-level judgments there. And the staggered post-2020 adoption of formal systems gives the mixed firms a readable time axis (Section V-B). What is not delivered in a mixed population is a per-signature verdict for the ambiguous middle — a limit of identification, not of workload. Exception management therefore holds in both settings; what changes is only the level at which exceptions are defined — the signature where reuse dominates, the accountant where practices are mixed. Because the cutoffs are tunable, a reviewer who wants higher specificity can tighten them (for example, cosine > 0.98 and dHash ≤ 3), trading a lighter caseload against the risk of missing noisier reused signatures — a trade-off we cannot tune against recall, since recall is unobservable here.
|
||||
|
||||
### C. Held-Out Benchmark: Firm A (a Known Positive)
|
||||
|
||||
Firm A — described by the interviews as a mainly-stamping firm, and kept out of the calibration — is our main benchmark. Because the interviews already identify it as a stamping firm, it is best read as a *quasi-positive institutional benchmark*: held out from calibration, but a known positive rather than a blinded out-of-sample test. What it can confirm is that the screen's measures move as expected on a firm independently believed to reuse images; what it cannot do is stand in for a blinded evaluation against ground-truth labels, which the corpus does not provide.
|
||||
|
||||
(1) Firm A's two measures against the baseline. Comparing Firm A's within-accountant similarities to those of Firms B/C/D (full record, 2013–2023²), Firm A's cos values are shifted toward 1.0 and its dHash distances toward 0 — the direction we would expect if a stored image is reused rather than re-signed. Concretely, Firm A's within-accountant cosine is centered at a median of 0.986 (mean 0.980) versus 0.959 (mean 0.954) for Firms B/C/D, and its smallest-dHash distance at a median of 2 (mean 2.7) versus 7 (mean 7.0); both shifts are in the reuse direction and overwhelmingly significant (Mann–Whitney U, p < 10⁻³⁰⁰ for each; two-sample Kolmogorov–Smirnov D = 0.60 for cosine and 0.57 for dHash). The decisive number is this: scored as a held-out (but not blinded) case — Firm A's signatures matched against unrelated accountants drawn from the clean 2013–2019 group — Firm A's per-signature cross-firm HC rate is 0.42% (154/36,552; Wilson 95% CI [0.36%, 0.49%]), at or below the clean reference ICCR of 0.59%. In other words, Firm A's cross-firm match rate sits at the level a clean inter-CPA comparison produces by chance — it is not elevated relative to the reference, and it is negligible beside the within-firm rate below — so the entire rise in Firm A's rate comes from matches with other Firm-A signatures, not from resemblance to other firms. The signal is inside the firm, not across firms. (Against the full-period BCD pool the same across-firm rate is 1.0%; the small difference reflects the post-2020 rise in baseline similarity of Section V-B. Both lie at the clean floor, two orders of magnitude below the within-firm rate that follows.)
|
||||
|
||||
> ² Restricting both groups to 2013–2019 gives essentially the same picture (Firm A cosine median 0.986, dHash 2; Firms B/C/D 0.957 and 7; Mann–Whitney p < 10⁻³⁰⁰ for each), confirming the contrast is not a post-2020 artifact.
|
||||
|
||||
Firm A's within-firm repeatability, against the other firms. On their own signatures, the HC rule fires on 82% of Firm A's, versus 24–35% for Firms B/C/D. We deliberately report these as raw within-accountant fire rates and do not divide them by the between-accountant clean floor: as Section III-E explains, that floor is the wrong null for a within-accountant question, so an "X× the floor" multiplier would overstate the gap. The firm-to-firm contrast in raw rates is what carries the result. A logistic regression of the per-signature HC flag on firm and pool size, with Firm A as the reference, gives odds ratios of 0.053, 0.010, and 0.027 for Firms B/C/D — one to two orders of magnitude lower (the odds ratio for log pool size is 4.01). Firm A stands alone, against a baseline of three firms that look alike.
|
||||
|
||||
Four further checks confirm the contrast is not an artifact of how the comparison pools are built, of the imaging-pipeline trend, or of any single year. First, pool size. Stratifying accountants by how many signatures they contribute and comparing within each stratum, Firm A's HC rate exceeds the other firms' at every level — 66% versus 20% for the smallest pools (under 50 signatures), rising to 76–84% versus 21–29% for larger pools. Even Firm-A accountants with few signatures to match against fire the rule far more often than B/C/D accountants with the same pool size; pool size raises the rate within every firm (the log-pool-size odds ratio of 4.01), but the firm gap dwarfs it and survives at fixed pool size, which rules out the "more signatures, more chances for an extreme match" explanation. Second, dependence among an accountant's own signatures. Re-estimating the gap with the bootstrap resampled at the accountant level (179 Firm-A accountants, 280 at Firms B/C/D) rather than treating signatures as independent, the Firm-A-minus-B/C/D difference in HC rate is 53.7 percentage points with a 95% interval of [49.5, 57.5] — accountant-level clustering widens the intervals the per-signature Wilson bounds give, but leaves the contrast far too large to be explained away. Third, the time trend and pipeline shift (Section V-B). Adding year fixed effects to the logistic regression — so the firm effect is identified within year, net of the 2020–2021 imaging-pipeline transition — leaves Firms B/C/D at 0.06–0.12 times Firm A's odds of an HC flag (odds ratios 0.116, 0.061, 0.070), still an order of magnitude lower once the common time trend is absorbed. Fourth, single-year dependence. Leaving out each calendar year in turn and recomputing, the Firm-A-minus-B/C/D gap stays within 53.1–54.9 percentage points (full-sample 53.7), so neither the high-reuse digital-native years (2022–2023) nor any earlier year drives it.
|
||||
|
||||

|
||||
|
||||
*Figure 4. Within-accountant similarities, Firm A vs Firms B/C/D: (a) cosine, (b) dHash. Firm A's mass sits near cosine = 1 and dHash = 0 (medians 0.986 / 2) against Firms B/C/D's 0.959 / 7; dashed lines mark the cuts (cosine 0.95; dHash 5), the dotted line the LH/UN crossover (0.8547). The held-out cross-firm HC rate (0.42%) sits at/below the clean reference ICCR (0.59%), while the within-Firm-A HC rate is 82% — the signal is inside the firm (annotation below panels).*
|
||||
|
||||
(2) Ranking accountants by similarity, in each period. Ranking every accountant in Firms A–D by a single within-accountant similarity score, separately for 2013–2019 and for 2020–2023, Firm A's accountants sit at the high-similarity (templated) end. A descriptive three-group summary of the two-measure space tells the same story: its high-cosine/low-dHash group holds 82.5% of Firm A's accountants and almost none of the others' (Table III). The period split confirms the expected pattern: Firm A's per-signature HC rate is at the top in both periods (80.3% in 2013–2019, 83.8% in 2020–2023), while Firms B/C/D move upward after 2020 as the formal systems came in — Firm B from 29.0% to 42.0%, Firm C from 21.6% to 26.7%, Firm D from 22.0% to 28.0% (see Section V-B).
|
||||
|
||||
**Table III — Firm by descriptive-group membership (whole corpus). The "high-cosine/low-dHash group" is the templated-end cluster of the three-group (K = 3) descriptive Gaussian-mixture partition of the accountant-level two-measure plane (Section V-C); membership is the cluster of maximum posterior probability for each accountant with at least ten signatures (437 accountants: 171/112/102/52 across Firms A–D). The groups are used for description only, never as operational labels.**
|
||||
|
||||
| Firm | Accountants | Share in the high-cosine/low-dHash group |
|
||||
|---|---|---|
|
||||
| Firm A | 171 | 82.5% |
|
||||
| Firm B | 112 | 0.0% |
|
||||
| Firm C | 102 | 1.0% |
|
||||
| Firm D | 52 | 1.9% |
|
||||
|
||||

|
||||
|
||||
*Figure 5. Per-accountant HC rate, ranked, one panel per period (2013–2019; 2020–2023), points colored by firm (accountants with ≥ 5 signatures in the period). Firm A (red) occupies the templated top of the ranking in both periods; Firms B/C/D rise after 2020 (HC rate B 29.0→42.0%, C 21.6→26.7%, D 22.0→28.0%; Firm A 80.3→83.8%), consistent with staggered formal-system adoption (Section V-B).*
|
||||
|
||||
(3) Applying the calibrated rule to Firm A, 2013–2023. Taking the operating point calibrated on Firms B/C/D in 2013–2019 and applying it across Firm A's full record, 81.70% of Firm A's signatures (82% rounded) land in HC (per signature; the full five-way breakdown is in Table IV). Read together with the interview fact that Firm A mainly uses overlay stamping, the system's firm-level output matches the practice the firm itself describes. We say this carefully: it is a match at the firm level, not a label on any single signature. We do not classify the individual signatures as non-hand-signed, because for any one signature the two measures cannot separate reuse from a shared scanning pipeline or a uniform house style (Section III-D).
|
||||
|
||||
**Table IV — Five-way breakdown by firm (whole corpus, 2013–2023; for reference; n = 150,442).**
|
||||
|
||||
| Firm | HC | MC | HSC | UN | LH | signatures |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Firm A | 81.70% | 10.76% | 0.05% | 7.35% | 0.14% | 60,448 |
|
||||
| Firm B | 34.56% | 35.88% | 0.29% | 28.95% | 0.32% | 34,248 |
|
||||
| Firm C | 23.75% | 41.44% | 0.38% | 33.97% | 0.47% | 38,613 |
|
||||
| Firm D | 24.51% | 29.33% | 0.22% | 45.28% | 0.66% | 17,133 |
|
||||
| Overall | 49.58% | 26.47% | 0.21% | 23.42% | 0.32% | 150,442 |
|
||||
|
||||
Reading the five-way mix across firms. Table IV is also the quantitative basis for the positioning in Section IV-B. At Firm A the ambiguous middle (MC + UN) is 18.1% — the screen reads this population almost cleanly, with four signatures in five settled outright. At Firms B/C/D the middle is 65–76% — the signature of a mixed population in which hand-signing and informal stamping coexist (Section III-A), where per-signature similarity is genuinely ambiguous. There the screen's deliverables move up one level (Section IV-B): the MC share (29–41% of these firms' signatures, against the 26.5% corpus-wide MC share) is demoted off the worklist, the accountant-level scores rank these firms' accountants alongside everyone else's, and the byte-identical signatures at these firms (117 of the 262) are threshold-free proof that reuse occurs there too. The per-signature mix stays ambiguous; the disposition does not.
|
||||
|
||||
Byte-identical signatures: direct evidence of reuse. Beyond the screening numbers, 262 signatures across the four firms are byte-for-byte identical to another signature — 145 of them at Firm A, spread across about fifty partners. Identical files cannot come from independent hand-signing, so their existence is direct, hard evidence that image reuse happens and that it concentrates at Firm A. These pairs are not a bookkeeping artifact: every one of the 262 matches a signature in a *different* report PDF (none is the same file double-counted), and 170 of the 262 fall in different filing months, so duplicate filings or corrected re-submissions of one report cannot explain them. One caveat belongs with this count, developed in Section V-B: most of the 262 (232) occur in the post-2020 digital-native era, where exact reuse is both easier and perfectly preserved, so the raw count is not a clean prevalence trend; the pipeline-independent core is the 30 in the pre-2021 pure-scan era (18 at Firm A), which scanning noise alone cannot produce. Because a byte-identical pair has cosine = 1 and dHash = 0, it lands in HC by definition; the rule's "100% capture" of this set is therefore tautological, and we do not read it as a sanity check or a lower bound on recall. We use byte-identity only for what it can show directly — that reuse occurs and where it concentrates — as a prevalence signal, not a measure of detector performance.
|
||||
|
||||
## V. Other Analyses
|
||||
|
||||
This section gathers analyses that support the design and test its robustness: (a) the diagnostic showing the data contain no natural cutoff — the premise the whole calibration rests on; (b) how the baseline behaves after 2020; and (c) sensitivity checks.
|
||||
|
||||
### A. Why the Data Contain No Natural Cutoff
|
||||
|
||||
This diagnostic backs the design choice announced in Section III-D and Section III-E: that no cutoff can be read off the data, so the operating point has to be set from an outside reference. The Hartigan dip test [37] rejects a single-peak shape for both measures at the Big-4-pooled accountant level (p < 5×10⁻⁴), which might look like a clean split into two groups. But that rejection comes from two side-effects. Once we remove the differences between firms (by centering each firm on its own mean) and the effect of the hash taking only whole-number values (by adding a small jitter to dHash), the single-peak shape comes back (median p = 0.35 over jitter seeds). Tested firm by firm, each Big-4 firm is already unimodal on both axes (Firm A p_cos = 0.99, p_dHash = 0.92; B/C/D pooled p_cos = 0.998, p_dHash = 0.91), so the pooled rejection is a between-firm location-shift artifact, not within-population bimodality. A density-smoothness test in the Burgstahler–Dichev / McCrary style [38], [39] finds no real break in either measure at the Big-4 scope (Appendix A.1 shows the apparent signature-level breaks drift with histogram bin width and sit inside the high-similarity region — a resolution artifact, not an antimode). So the data hold no real gap; per-signature similarity is best read as one continuous spread of quality, not two separate classes. This is exactly why the operating point is set from an outside reference (Section III-E) rather than read off the data, and why the three groups used for description in Section IV-C are treated as a summary of composition, not as real mechanisms.
|
||||
|
||||
A property of the cosine measure reinforces this and explains why the rule never leans on cosine alone. On these fixed-size, white-padded, ImageNet-normalized crops the within-accountant cosine is compressed into a narrow band at the top of its range: 97.7% of signatures score above 0.90, the median is 0.969, and only 0.3% fall below 0.85 (Appendix). Two signatures of the same accountant are highly cosine-similar whether the hand is steady or the image is reused, because the shared crop geometry and normalization contribute a common-structure baseline before any signature content is compared. The high cosine cut (0.95) therefore sits *inside* this saturated region — about three-quarters of signatures lie above it — so cosine on its own separates almost nothing; the structural dHash measure does the discriminating, which is why HC requires both and the cosine-only HSC band carries no evidential weight (Section III-D). We do not try to decompose this cosine baseline into its preprocessing and genuine-style parts here: doing so cleanly would mean re-extracting features with the padding and normalization ablated, which we flag as the way to quantify the preprocessing contribution and as a construct-validity check for future work.
|
||||
|
||||
### B. Time Trend and the Firm–Pipeline Confound (Secondary)
|
||||
|
||||
Looking only at Firms B/C/D, the strict rule's chance rate rises after 2020 (per comparison from 1.0×10⁻⁵ to 3.6×10⁻⁵; per signature from 0.59% to 1.05%), and the deployed HC rate rises in parallel (Firm B 29.0→42.0%, Firm C 21.6→26.7%, Firm D 22.0→28.0% across the two periods, Section IV-C). The rise is heterogeneous in timing rather than a common step. Tracing the yearly HC rate, Firm C's increase is concentrated in 2022 (about 18% through 2021, then ~30% in 2022 and ~40% in 2023) and Firm B's mainly in 2023 (about 33% in 2022, ~54% in 2023), while Firm D rises gradually with no visible step; Firm A, by contrast, is already high throughout the decade (80.3→83.8%) with no adoption-like jump — consistent with the interviews' account of long-standing stamping. This firm-by-firm staggering is what one would expect from progressive, independent adoption of formal signing systems (Section III-A), and it is why we limit the calibration to the pre-2020 years. Table II-c gives the full five-way breakdown by firm for the 2020–2023 deployment period, as a companion to the calibration-period Table II-b and for direct cross-checking against the proportions quoted here and in Section IV-C.
|
||||
|
||||
**Table II-c — Five-way breakdown by firm, deployment period (Firms A–D, 2020–2023).**
|
||||
|
||||
| Firm | HC | MC | HSC | UN | LH | signatures |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Firm A | 83.84% | 9.13% | 0.04% | 6.88% | 0.11% | 23,898 |
|
||||
| Firm B | 42.01% | 31.24% | 0.16% | 26.31% | 0.28% | 14,571 |
|
||||
| Firm C | 26.74% | 40.55% | 0.40% | 31.80% | 0.51% | 16,164 |
|
||||
| Firm D | 27.98% | 28.85% | 0.24% | 42.42% | 0.51% | 7,188 |
|
||||
|
||||
We deliberately stop short of reading this as a *detected* e-signing effect, because of a confound these data cannot break: firm identity — and period within a firm — bundles signing practice together with the entire imaging pipeline, and that pipeline demonstrably changes across the decade. We audited the production provenance of a stratified sample of 880 report PDFs (20 per firm-year) from their embedded metadata and page structure. The shift is stark (Table V): through 2020, reports are overwhelmingly plain scanned rasters — 70–85% in the early years carry no text layer at all, and their PDF metadata names the scanning hardware directly (for example "Fuji Xerox D125" and "ApeosPort-IV 7080") — whereas from 2021 plain scans collapse to about 1–2% as firms move to OCR'd and digital-native production. The two similarity measures are therefore computed on a substrate that itself transforms around 2020–2021, exactly when the baseline firms' similarity rises; firms also differ from one another in this respect (Firm A adopts digital-native output earliest, Firm C latest), though the cross-firm gap is much smaller than the temporal one. A post-2020 rise in similarity could thus come from this coincident pipeline change just as easily as from a change in how signatures are applied (Section III-D), and with no labels and no externally-dated adoption events the two are not separable here.
|
||||
|
||||
**Table V — Imaging-pipeline audit: production type by year (stratified sample, 880 PDFs, 20 per firm-year). "Scanned" = no extractable text layer; "digital-native" = text-based PDF with embedded image objects; the remainder are scanned-then-OCR'd.**
|
||||
|
||||
| Year | Scanned % | Digital-native % | Year | Scanned % | Digital-native % |
|
||||
|---|---|---|---|---|---|
|
||||
| 2013 | 82 | 0 | 2019 | 56 | 0 |
|
||||
| 2014 | 76 | 0 | 2020 | 52 | 0 |
|
||||
| 2015 | 85 | 0 | 2021 | 1 | 7 |
|
||||
| 2016 | 70 | 2 | 2022 | 2 | 16 |
|
||||
| 2017 | 50 | 0 | 2023 | 2 | 30 |
|
||||
| 2018 | 55 | 0 | | | |
|
||||
|
||||
This same transition qualifies the byte-identity evidence (Section IV-C), which we flag rather than let the raw count mislead. Of the 262 byte-identical signatures, 232 fall in the digital-native era (2021–2023), where embedding a discrete signature image makes exact reuse both easy to do and perfectly preserved — so the post-2020 surge in byte-identical pairs is inflated by detectability and should not be read as a purely behavioral increase. The pipeline-robust core is the 30 byte-identical signatures in the pre-2021 pure-scan era, 18 of them at Firm A: two independently hand-signed pages, separately scanned, cannot yield byte-identical crops, so these are direct evidence of digital image reuse that predates the digital-native transition and concentrates at Firm A. This is also why Firm A's elevation, present throughout the scanned years, cannot be an artifact of digital-native embedding.
|
||||
|
||||
The clean way to separate them would be an event study that aligns each firm to its own externally-documented e-signing adoption date and absorbs static firm differences and common-year shocks with firm and year fixed effects; a within-firm jump locked to each firm's own adoption month would be evidence for signing practice over static pipeline. We do not possess those adoption dates — and inferring them from the very HC series we would then test would be circular (Section III-E) — so we flag this event study as the natural next step rather than a result we can claim here. For this paper the trend serves only its narrower purpose: it justifies the pre-2020 calibration window and stands as a robustness check, not as a causal finding.
|
||||
|
||||
### C. Sensitivity and Robustness
|
||||
|
||||
We summarize the robustness checks here; full detail is in the supplementary materials.
|
||||
|
||||
How sensitive the operating point is. Right around the HC cutoff the per-signature firing rate changes quickly — its local slope is about 25× the median across a cosine sweep and about 3.8× across a dHash sweep — which confirms that the HC point is a chosen, specificity-anchored operating point rather than a natural gap.
|
||||
|
||||
A single slope understates how the rule behaves, so we map the full surface rather than defend one cut. Figure 6 plots, over the entire (cosine cut × dHash cut) plane, the clean-group flag rate (panel a) and the Firm A − B/C/D flag-rate contrast (panel b), and neither view favors the chosen cut by construction. First, the surfaces are smooth: there is no cliff at (0.95, dHash ≤ 5), so the operating point is a readable choice on a continuous trade-off rather than a discovered boundary (Section V-A), and an operator who wants a tighter floor can move toward higher cosine and lower dHash and read the consequence off the surface. Second, the firm contrast is not an artifact of the threshold: it exceeds 45 percentage points across a broad region of low-dHash, high-cosine cuts and in fact grows as the cut tightens (for example 58 pp at cosine 0.97, dHash ≤ 3), so the deliberately looser HC point trades a few points of contrast for catching more reuse, not the reverse. The same surface makes the weakness of the cosine-only direction explicit: extending the structural cut to the MC bound (dHash ≤ 15) roughly halves the contrast (to about 27 pp) while sharply inflating the clean-group flag rate. That is precisely why the MC band is only advisory and the cosine-only HSC band carries no weight (Section III-D): the partition is not drawn to flatter the narrative, and the surface shows directly where each band earns its keep and where it does not.
|
||||
|
||||

|
||||
|
||||
*Figure 6. Sensitivity surface of the deployed rule over the two-measure threshold plane (Big-4, n = 150,442). (a) Clean-group (B/C/D) flag rate at each (cosine cut, dHash cut); the chosen HC operating point (star) sits in a low-rate, high-specificity region with no cliff. (b) Firm A minus B/C/D flag-rate contrast (percentage points); the contrast exceeds 45 pp across a broad low-dHash, high-cosine band and weakens toward the MC bound (dHash ≤ 15, dotted), so the operating point is not a cherry-picked threshold and the MC band is visibly the less discriminating region.* The MC/HSC boundary at dHash = 15 sits in a flat (saturating) region, where moving the line adds flagged cases without adding specificity; this is a further reason to treat the MC band as advisory (Section IV-B).
|
||||
|
||||
Leaving out one firm at a time. A two-group fit is unstable across firms — its boundary is basically a "Firm A versus the rest" divider — while a three-group fit keeps a stable shape (its low-cosine/high-dHash group drifts by at most 0.005 in cosine) but a membership that shifts with the mix of firms (by up to 12.8 percentage points). So we use the groups only as descriptions, never as operational labels.
|
||||
|
||||
Crossover scope. The low cosine cut is the same-vs-different-accountant cosine crossover; recomputing it across scopes moves it by at most 0.025 — 0.8547 on the calibration cell (the primary value; Section IV-A), 0.8367 corpus-wide, 0.8489 on the all-period baseline firms, 0.8302 with the non-Big-4 firms added — and because the cut affects only the UN/LH boundary, switching among these scopes changes no HC/MC/HSC result and shifts the UN/LH split by at most 0.4 percentage points per firm. We use the calibration-cell value as primary for held-out discipline and report the others as robustness.
|
||||
|
||||
The same-pair variant. A reader may worry that the deployed rule is a *derived* statistic rather than an observation: the cosine maximum and the dHash minimum are each taken over the accountant's pool and can originate from different partner signatures, so the high-confidence region might in principle be assembled from two unrelated extrema. We therefore recompute the rule under the strict *same-pair* construction, where a single partner signature must satisfy both inequalities at once (Section III-D), and report it in the main text rather than the supplement. Two views agree. First, the within-firm concentration of cross-accountant matches is higher under same-pair (97.0–99.96% across the four firms) than under the deployed any-pair rule (76.7–98.8%). Second, and more directly, the per-signature HC flag rate — the quantity the any-pair concern targets — behaves the same way (Table VI): requiring one partner to satisfy both inequalities lowers every firm's rate, as expected, but it *widens* the firm gap rather than narrowing it. Firm A still fires on a majority of its own signatures (57.3%) while the baseline firms fall to 5–9%, so the Firm-A-to-baseline ratio rises from about 2.4–3.4× under any-pair to about 6.4–10.8× under same-pair. The high-confidence region is therefore not an artifact of combining extrema from different partner signatures; pushed to the stricter event, the structure gets stronger.
|
||||
|
||||
**Table VI — HC flag rate by firm under the deployed any-pair rule and the strict same-pair rule.**
|
||||
|
||||
| Firm | Signatures | Any-pair HC | Same-pair HC |
|
||||
|---|---|---|---|
|
||||
| Firm A | 60,448 | 81.7% | 57.3% |
|
||||
| Firm B | 34,248 | 34.6% | 9.0% |
|
||||
| Firm C | 38,613 | 23.7% | 5.3% |
|
||||
| Firm D | 17,133 | 24.5% | 7.7% |
|
||||
| All Big-4 | 150,442 | 49.6% | 27.3% |
|
||||
|
||||
Each gate adds specificity. On the all-four-firm pool the cosine gate alone fires per comparison at 6.0×10⁻⁴; adding the structural gate multiplies this by 0.234 (the conditional ICCR of dHash ≤ 5 given cos > 0.95), giving the joint 1.4×10⁻⁴. Each axis contributes specificity beyond the other — quantitative support for the two-gate design over either measure alone (Section I, Section III-D).
|
||||
|
||||
Which network we use. We compare ResNet-50 against VGG-16 and EfficientNet-B0 under the same preprocessing and L2 normalization (Appendix A; supplementary backbone-ablation table). EfficientNet-B0 gives the largest intra/inter separation (Cohen's d = 0.707) but also the widest descriptor spread (intra std 0.123 vs ResNet-50's 0.098); VGG-16 is worst on every key metric despite its larger 4096-dim features. ResNet-50 is the best overall balance: its Cohen's d (0.669) is competitive, its tighter distributions give more stable per-signature behavior, it yields the highest Firm A all-pairs 1st-percentile similarity (0.543), and its 2048-dim features are a practical compromise for processing 182K+ signatures. The comparison supports ResNet-50.
|
||||
|
||||
### D. Threats to Validity
|
||||
|
||||
For the reader's convenience we collect the main threats to validity in one place, each with a pointer to where it is treated and, where relevant, the direction of its bias. They are consequences of working without labels, and we state them as limitations rather than dissolve them.
|
||||
|
||||
1. *No signature-level ground truth.* The archive labels no signature as hand-signed or reused, so we report no recall, precision, ROC-AUC, or false-rejection rate, and every rate is a chance rate, not an error rate (Section III-D, Section VI).
|
||||
2. *Wrong null for the reuse question.* The ICCR is a between-accountant coincidence rate; the within-accountant false-positive rate the question needs is not estimable and the ICCR is not even a bound on it, so "X× the floor" comparisons are avoided as anti-conservative (Section III-E, Section IV-C).
|
||||
3. *Reference-group contamination / circular selection.* The clean floor is conditional on the reference group truly being clean; undetected reuse there would only raise the floor, biasing the Firm-A contrast conservatively, and the floor does not hinge on any single baseline firm (Section III-E).
|
||||
4. *Pool-size and extremal dependence.* The rule takes a maximum over a pool, so larger pools mechanically raise fire rates; the firm contrast nonetheless holds within every pool-size stratum and under accountant-clustered resampling (Section IV-C).
|
||||
5. *Firm–pipeline confound.* Firm identity bundles signing practice with the imaging pipeline (crop geometry and reuse rates differ by firm), and internal timing cannot separate the two without externally-dated adoption events; a fixed-effects event study is the natural next step (Section V-B).
|
||||
6. *Preprocessing and construct validity.* Padding and ImageNet normalization compress cosine into a narrow high band, so cosine alone discriminates little and the rule relies on the structural measure; a padding/normalization ablation is needed to quantify the preprocessing contribution (Section V-A).
|
||||
7. *Generalizability.* Calibration is on a Chinese-signature corpus from one jurisdiction with a specific pipeline; the operating point is a starting reference for comparable pipelines, not a transplantable constant, and requires recalibration elsewhere (Section III-D, Section VI).
|
||||
8. *Non-reproducible corroboration and an unrun protocol.* The interviews are self-reported and not reproducible, so agreement with them shows consistency with domain knowledge, not measured accuracy (Section III-A); and the review protocol of Section IV-B is a designed procedure whose validating first run remains future work.
|
||||
|
||||
## VI. Conclusion
|
||||
|
||||
We have presented a label-free, anchor-calibrated way to screen for non-hand-signed signatures in large numbers of audit reports. It has three working parts — a pipeline that takes raw PDFs through page-finding, detection, feature extraction, and a two-measure similarity step; a pair of measures that separate style consistency from image reproduction; and, in place of a natural cutoff we do not have and labeled data we cannot get, a calibration based on how often the rule fires by chance in a clean reference group. That calibration yields both a between-accountant specificity proxy and a concrete operating point: the high-confidence rule almost never fires by chance on the clean group, so it is a usable, highly specific screen, with a defined, bounded human-review protocol (Section IV-B) for the advisory and uncertain cases. Operationally the screen earns its keep in two ways: run over an archive, it discovers where reuse concentrates; and it keeps human review at the scale of exceptions in both kinds of population — settling most signatures directly where reuse dominates, and, where practices are mixed, demoting the low-specificity band, ranking accountants, and confirming the byte-identical cases, withholding only the per-signature verdict for the ambiguous middle. We report the category proportions that make that distinction concrete. Because it is calibrated on a large Chinese-signature corpus and uses script-agnostic image descriptors, the rule offers a practical starting reference for comparable Chinese-signature pipelines and, in principle, an approach portable to other scripts — subject in each case to recalibration on the new setting. Held out as a known-positive benchmark, one firm stands alone in how alike its own signatures are, its output matches the stamping practice the firm itself describes, and byte-identical signatures give direct evidence that reuse happens and concentrates there.
|
||||
|
||||
The limits are built into working without labels, and we have stated them alongside the design. There is no signature-level ground truth, so we report no false-rejection rate, recall, ROC-AUC, or precision; every rate we give is a between-accountant chance rate read as a proxy for specificity, not a true false-acceptance rate, and not even a bound on the within-accountant false-positive rate the reuse question would need (Section III-E). The contrast between firms is something the method can see, not a finding about why the signatures look alike: for any single signature, the two measures cannot separate reuse from a shared scanning pipeline or a uniform house style, and for Firms B/C/D we make no claim about firm practice at all. Whether firm-level signing patterns matter for audit quality is a question for a dedicated companion study — one this screening points toward, together with the low-presence character of proxy-executed stamping shown in the behavioral literature, but one that similarity alone cannot settle.
|
||||
|
||||
Four directions follow. First, a set-level reading of each accountant: judging the shape of an accountant's whole signature set — a tight cluster that recurs near-identically across reports and years (the signature of a stored image) versus a dispersed cloud (the signature of a hand) — instead of per-signature extrema. This would collapse much of the remaining middle into a few per-accountant cluster decisions, and it is the natural tool for separating the mixed signers of the baseline firms, whose sets may contain both a tight recurring sub-cluster and a dispersed remainder if both practices are present. We view this as the highest-value methodological extension, while noting honestly that it narrows but does not remove the fundamental ambiguity: a very steady hand and a noisy reused image can still meet in the middle of any set-level statistic. A first-pass probe on the calibration cell is consistent with this caution — across the 206 of the 226 Firms-B/C/D 2013–2019 accountants with enough signatures for a set-level shape to be estimated, the within-accountant similarity forms a continuum that piles up just below the high-similarity cut rather than splitting into a tight reused cluster and a dispersed hand-signed cloud (no accountant shows a tight-versus-remainder cosine gap above 0.10), so the no-natural-cutoff structure of Section V-A recurs at the accountant level; we therefore treat set-level adjudication as a research direction rather than a ready robustness result. Second, executing the review protocol of Section IV-B on a bounded sample — its first run — would both test the protocol's expected discriminating behavior and accumulate the small human-labeled set that permits supervised validation and direct error rates. Third, image-acquisition metadata (scanner identifiers, PDF-generator fingerprints, compression markers) adds a provenance axis that could help resolve the pipeline-versus-reuse ambiguity similarity alone cannot; we confirmed this metadata survives in the present corpus rather than being flattened by the platform, though its discriminative power remains to be validated (Section IV-B, Move 4). Fourth, the audit-quality question itself: whether firm-level signing patterns correlate with audit outcomes, for which this screening supplies the measurement layer.
|
||||
|
||||
## Appendix A. Supplementary Diagnostic Detail
|
||||
|
||||
### A.1. BD/McCrary Bin-Width Sensitivity (Signature Level)
|
||||
|
||||
The main text (Section III-D, Section V-A) treats the Burgstahler–Dichev / McCrary discontinuity procedure [38], [39] as a density-smoothness diagnostic rather than as a threshold estimator. This subsection documents the empirical basis for that framing by sweeping the bin width across four (variant, bin-width) panels: Firm A and full-sample, each in the cosine and dHash direction.
|
||||
|
||||
**Table A.I. BD/McCrary bin-width sensitivity (two-sided α = 0.05, |Z| > 1.96).**
|
||||
|
||||
| Variant | n | Bin width | Best transition | z_below | z_above |
|
||||
|---|---|---|---|---|---|
|
||||
| Firm A cosine (sig-level) | 60,448 | 0.003 | 0.9870 | −2.81 | +9.42 |
|
||||
| Firm A cosine (sig-level) | 60,448 | 0.005 | 0.9850 | −9.57 | +19.07 |
|
||||
| Firm A cosine (sig-level) | 60,448 | 0.010 | 0.9800 | −54.64 | +69.96 |
|
||||
| Firm A cosine (sig-level) | 60,448 | 0.015 | 0.9750 | −85.86 | +106.17 |
|
||||
| Firm A dHash (sig-level) | 60,448 | 1 | 2.0 | −4.69 | +10.01 |
|
||||
| Firm A dHash (sig-level) | 60,448 | 2 | no transition | — | — |
|
||||
| Firm A dHash (sig-level) | 60,448 | 3 | no transition | — | — |
|
||||
| Full-sample cosine (sig-level) | 168,740 | 0.003 | 0.9870 | −3.21 | +8.17 |
|
||||
| Full-sample cosine (sig-level) | 168,740 | 0.005 | 0.9850 | −8.80 | +14.32 |
|
||||
| Full-sample cosine (sig-level) | 168,740 | 0.010 | 0.9800 | −29.69 | +44.91 |
|
||||
| Full-sample cosine (sig-level) | 168,740 | 0.015 | 0.9450 | −11.35 | +14.85 |
|
||||
| Full-sample dHash (sig-level) | 168,740 | 1 | 2.0 | −6.22 | +4.89 |
|
||||
| Full-sample dHash (sig-level) | 168,740 | 2 | 10.0 | −7.35 | +3.83 |
|
||||
| Full-sample dHash (sig-level) | 168,740 | 3 | 9.0 | −11.05 | +45.39 |
|
||||
|
||||
Two patterns are visible. First, the procedure consistently identifies a "transition" under every bin width, but the location drifts monotonically with bin width (Firm A cosine: 0.987 → 0.985 → 0.980 → 0.975 as bin width grows from 0.003 to 0.015; full-sample dHash: 2 → 10 → 9 as bin width grows from 1 to 3), and the Z statistics inflate superlinearly with bin width because wider bins aggregate more mass and shrink the per-bin standard error on a very large sample. Both features are characteristic of a histogram-resolution artifact rather than a genuine density discontinuity. Second, the candidate transitions all locate inside the high-similarity region (cosine ≥ 0.975, dHash ≤ 10) rather than at a between-mode boundary. Taken together, the signature-level BD/McCrary transitions are not a threshold in the usual sense — they are histogram-resolution-dependent local density anomalies inside the high-similarity descriptor region rather than between modes — which supports using BD/McCrary as a density-smoothness diagnostic, not a threshold estimator (Section V-A).
|
||||
|
||||
### A.2. Diagnostic Summary
|
||||
|
||||
The unsupervised-diagnostic strategy is a set of complementary checks, each addressing one specific failure mode of an unsupervised screening classifier under an explicitly disclosed untested assumption.
|
||||
|
||||
**Table A.II. Diagnostics, failure mode addressed, and disclosed untested assumption (abridged).**
|
||||
|
||||
| Diagnostic | Failure mode addressed | Disclosed untested assumption |
|
||||
|---|---|---|
|
||||
| Composition decomposition (Section V-A) | Whether descriptor multimodality is within-population (mechanism) or between-group (composition + integer artifact); p_median = 0.35 under joint firm-mean centering + integer-tie jitter | Integer-tie jitter and firm-mean centering are unbiased over the descriptor support |
|
||||
| Per-comparison ICCR (Section IV-A) | Pair-level specificity proxy under a random-pair negative anchor, on the BCD baseline | Inter-CPA pairs are negative; addressed by anchoring on B/C/D and holding Firm A out |
|
||||
| Pool-normalised per-signature ICCR (Section IV-A) | Deployed-rule specificity proxy at per-signature unit, accounting for pool size | As above + pool replacement preserves the negative-anchor property |
|
||||
| Document-level ICCR (Section IV-A) | Operational alarm-rate proxy at per-document unit (HC and HC+MC) | As above |
|
||||
| Firm-heterogeneity logistic regression (Section IV-C) | Multiplicative effect of firm membership on per-signature rate, controlling for pool size | Observations clustered by CPA/firm; cluster-robust SEs are a future check |
|
||||
| Cross-firm hit matrix (Section IV-C, Section V-C) | Concentration of inter-CPA collisions within source firm | Concentration depends on deployed-rule semantics (same-pair 97.0–99.96% vs any-pair 76.7–98.8%) |
|
||||
| Alert-rate sensitivity sweep (Section V-C) | Local sensitivity of the deployed rule to threshold perturbation | Gradient comparison is descriptive, not a formal plateau test |
|
||||
| Convergent score Spearman ranking (Section IV-B) | Internal consistency of three feature-derived per-CPA scores | Scores share inputs; not statistically independent |
|
||||
| Pixel-identical positive capture (Section IV-C) | Prevalence evidence that reuse occurs and where it concentrates | Anchor is tautologically captured by any reasonable threshold; not read as a recall or performance measure |
|
||||
|
||||
## Appendix B. Reproducibility Materials
|
||||
|
||||
The full table-to-script provenance mapping, script source code, and report artifacts for every numerical table and figure in this paper are provided in the supplementary materials. Scripts run deterministically under fixed random seeds documented there (the inter-CPA candidate sampler uses seed 42 and a retry-loop matching the canonical samplers; CPA-block bootstraps use 1,000 replicates); reviewer reproduction should re-emit artifacts from the listed scripts rather than rely on any local path layout. The calibration baseline (BCD 2013–2019), the contamination-comparison scope (all-Big-4), the Firm-A out-of-sample scoring, and the five-way classification are all emitted by the same canonical pipeline so that the headline numbers in Tables I, II, II-b, and IV reproduce bit-for-bit.
|
||||
|
||||
## References
|
||||
|
||||
*References follow IEEE numeric style; entries [41]–[45] are the behavioral-science and Chinese-script works added in this draft.*
|
||||
|
||||
[1] Taiwan Certified Public Accountant Act (會計師法), Art. 4; FSC Attestation Regulations (查核簽證核准準則), Art. 6.
|
||||
|
||||
[2] S.-H. Yen, Y.-S. Chang, and H.-L. Chen, "Does the signature of a CPA matter? Evidence from Taiwan," *Res. Account. Regul.*, vol. 25, no. 2, pp. 230–235, 2013.
|
||||
|
||||
[3] J. Bromley et al., "Signature verification using a Siamese time delay neural network," in *Proc. NeurIPS*, 1993.
|
||||
|
||||
[4] S. Dey et al., "SigNet: Convolutional Siamese network for writer independent offline signature verification," arXiv:1707.02131, 2017.
|
||||
|
||||
[5] H.-H. Kao and C.-Y. Wen, "An offline signature verification and forgery detection method based on a single known sample and an explainable deep learning approach," *Appl. Sci.*, vol. 10, no. 11, p. 3716, 2020.
|
||||
|
||||
[6] H. Li et al., "TransOSV: Offline signature verification with transformers," *Pattern Recognit.*, vol. 145, p. 109882, 2024.
|
||||
|
||||
[7] S. Tehsin et al., "Enhancing signature verification using triplet Siamese similarity networks in digital documents," *Mathematics*, vol. 12, no. 17, p. 2757, 2024.
|
||||
|
||||
[8] P. Brimoh and C. C. Olisah, "Consensus-threshold criterion for offline signature verification using CNN learned representations," arXiv:2401.03085, 2024.
|
||||
|
||||
[9] N. Woodruff et al., "Fully-automatic pipeline for document signature analysis to detect money laundering activities," arXiv:2107.14091, 2021.
|
||||
|
||||
[10] S. Abramova and R. Böhme, "Detecting copy-move forgeries in scanned text documents," in *Proc. Electronic Imaging*, 2016.
|
||||
|
||||
[11] Y. Li et al., "Copy-move forgery detection in digital image forensics: A survey," *Multimedia Tools Appl.*, 2024.
|
||||
|
||||
[12] Y. Jakhar and M. D. Borah, "Effective near-duplicate image detection using perceptual hashing and deep learning," *Inf. Process. Manage.*, p. 104086, 2025.
|
||||
|
||||
[13] E. Pizzi et al., "A self-supervised descriptor for image copy detection," in *Proc. CVPR*, 2022.
|
||||
|
||||
[14] L. G. Hafemann, R. Sabourin, and L. S. Oliveira, "Learning features for offline handwritten signature verification using deep convolutional neural networks," *Pattern Recognit.*, vol. 70, pp. 163–176, 2017.
|
||||
|
||||
[15] E. N. Zois, D. Tsourounis, and D. Kalivas, "Similarity distance learning on SPD manifold for writer independent offline signature verification," *IEEE Trans. Inf. Forensics Security*, vol. 19, pp. 1342–1356, 2024.
|
||||
|
||||
[16] L. G. Hafemann, R. Sabourin, and L. S. Oliveira, "Meta-learning for fast classifier adaptation to new users of signature verification systems," *IEEE Trans. Inf. Forensics Security*, vol. 15, pp. 1735–1745, 2020.
|
||||
|
||||
[17] H. Farid, "Image forgery detection," *IEEE Signal Process. Mag.*, vol. 26, no. 2, pp. 16–25, 2009.
|
||||
|
||||
[18] F. Z. Mehrjardi, A. M. Latif, M. S. Zarchi, and R. Sheikhpour, "A survey on deep learning-based image forgery detection," *Pattern Recognit.*, vol. 144, art. 109778, 2023.
|
||||
|
||||
[19] J. Luo et al., "A survey of perceptual hashing for multimedia," *ACM Trans. Multimedia Comput. Commun. Appl.*, vol. 21, no. 7, 2025.
|
||||
|
||||
[20] D. Engin et al., "Offline signature verification on real-world documents," in *Proc. CVPRW*, 2020.
|
||||
|
||||
[21] D. Tsourounis et al., "From text to signatures: Knowledge transfer for efficient deep feature learning in offline signature verification," *Expert Syst. Appl.*, vol. 189, art. 116136, 2022.
|
||||
|
||||
[22] B. Chamakh and O. Bounouh, "A unified ResNet18-based approach for offline signature classification and verification across multilingual datasets," *Procedia Comput. Sci.*, vol. 270, pp. 4024–4033, 2025.
|
||||
|
||||
[23] A. Babenko, A. Slesarev, A. Chigorin, and V. Lempitsky, "Neural codes for image retrieval," in *Proc. ECCV*, 2014, pp. 584–599.
|
||||
|
||||
[24] S. Bai et al., "Qwen2.5-VL technical report," arXiv:2502.13923, 2025.
|
||||
|
||||
[25] Ultralytics, "YOLO11 documentation," 2024. [Online]. Available: https://docs.ultralytics.com
|
||||
|
||||
[26] K. He, X. Zhang, S. Ren, and J. Sun, "Deep residual learning for image recognition," in *Proc. CVPR*, 2016.
|
||||
|
||||
[27] N. Krawetz, "Kind of like that," The Hacker Factor Blog, Jan. 21, 2013. [Online]. Available: https://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html
|
||||
|
||||
[28] B. W. Silverman, *Density Estimation for Statistics and Data Analysis*. London: Chapman & Hall, 1986.
|
||||
|
||||
[29] J. Cohen, *Statistical Power Analysis for the Behavioral Sciences*, 2nd ed. Hillsdale, NJ: Lawrence Erlbaum, 1988.
|
||||
|
||||
[30] Z. Wang, A. C. Bovik, H. R. Sheikh, and E. P. Simoncelli, "Image quality assessment: From error visibility to structural similarity," *IEEE Trans. Image Process.*, vol. 13, no. 4, pp. 600–612, 2004.
|
||||
|
||||
[31] J. V. Carcello and C. Li, "Costs and benefits of requiring an engagement partner signature: Recent experience in the United Kingdom," *The Accounting Review*, vol. 88, no. 5, pp. 1511–1546, 2013.
|
||||
|
||||
[32] A. D. Blay, M. Notbohm, C. Schelleman, and A. Valencia, "Audit quality effects of an individual audit engagement partner signature mandate," *Int. J. Auditing*, vol. 18, no. 3, pp. 172–192, 2014.
|
||||
|
||||
[33] W. Chi, H. Huang, Y. Liao, and H. Xie, "Mandatory audit partner rotation, audit quality, and market perception: Evidence from Taiwan," *Contemp. Account. Res.*, vol. 26, no. 2, pp. 359–391, 2009.
|
||||
|
||||
[34] J. Redmon, S. Divvala, R. Girshick, and A. Farhadi, "You only look once: Unified, real-time object detection," in *Proc. CVPR*, 2016, pp. 779–788.
|
||||
|
||||
[35] J. Zhang, J. Huang, S. Jin, and S. Lu, "Vision-language models for vision tasks: A survey," *IEEE Trans. Pattern Anal. Mach. Intell.*, vol. 46, no. 8, pp. 5625–5644, 2024.
|
||||
|
||||
[36] H. B. Mann and D. R. Whitney, "On a test of whether one of two random variables is stochastically larger than the other," *Ann. Math. Statist.*, vol. 18, no. 1, pp. 50–60, 1947.
|
||||
|
||||
[37] J. A. Hartigan and P. M. Hartigan, "The dip test of unimodality," *Ann. Statist.*, vol. 13, no. 1, pp. 70–84, 1985.
|
||||
|
||||
[38] D. Burgstahler and I. Dichev, "Earnings management to avoid earnings decreases and losses," *J. Account. Econ.*, vol. 24, no. 1, pp. 99–126, 1997.
|
||||
|
||||
[39] J. McCrary, "Manipulation of the running variable in the regression discontinuity design: A density test," *J. Econometrics*, vol. 142, no. 2, pp. 698–714, 2008.
|
||||
|
||||
[40] A. P. Dempster, N. M. Laird, and D. B. Rubin, "Maximum likelihood from incomplete data via the EM algorithm," *J. R. Statist. Soc. B*, vol. 39, no. 1, pp. 1–38, 1977.
|
||||
|
||||
[41] E. Y. Chou, "Paperless and soulless: E-signatures diminish the signer's presence and decrease acceptance," *Social Psychological and Personality Science*, vol. 6, no. 3, pp. 343–351, 2015.
|
||||
|
||||
[42] E. Y. Chou, "What's in a name? The toll e-signatures take on individual honesty," *Journal of Experimental Social Psychology*, vol. 61, pp. 84–95, 2015.
|
||||
|
||||
[43] K. Tzelios and L. A. Williams, "The psychological impact of digital signatures: A multistudy replication," *Technology, Mind, and Behavior*, vol. 1, no. 2, 2020.
|
||||
|
||||
[44] S. Pal, M. Blumenstein, and U. Pal, "Non-English and non-Latin signature verification systems: A survey," in *Proc. 1st Int. Workshop on Automated Forensic Handwriting Analysis (AFHA)*, 2011, pp. 1–5.
|
||||
|
||||
[45] X. Chen, "Extraction and analysis of the width, gray scale and radian in Chinese signature handwriting," *Forensic Science International*, vol. 255, pp. 123–132, 2015.
|
||||
|
||||
## Declarations
|
||||
|
||||
**Conflict of interest.** The authors declare no conflict of interest with Firm A, Firm B, Firm C, or Firm D, or with any other entity referenced in this work.
|
||||
|
||||
**Data availability.** All audit reports analyzed in this study were obtained from the Market Observation Post System (MOPS) operated by the Taiwan Stock Exchange Corporation, a publicly accessible regulatory disclosure platform. The CPA registry used to map signatures to certifying CPAs is publicly available. The reproducibility scripts and trained model weights are provided in the supplementary materials; signature-image release is subject to the firm-anonymization constraints of Section III-A (a de-identified subset and the per-table provenance mapping are included, with the full image set available to reviewers under the platform's public-data terms).
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,79 @@
|
||||
import sqlite3, numpy as np
|
||||
from collections import defaultdict
|
||||
from scipy.stats import gaussian_kde
|
||||
DB='/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A='勤業眾信聯合'; BIG4=('勤業眾信聯合','安侯建業聯合','資誠聯合','安永聯合')
|
||||
SEED=42; POP=np.array([bin(i).count('1') for i in range(256)],dtype=np.uint8)
|
||||
def load():
|
||||
c=sqlite3.connect(f'file:{DB}?mode=ro',uri=True)
|
||||
r=c.execute("""SELECT s.assigned_accountant,a.firm,s.source_pdf,s.feature_vector,s.dhash_vector,
|
||||
CAST(substr(s.year_month,1,4) AS INT) FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""").fetchall()
|
||||
c.close(); return r
|
||||
def crossover(keep,label):
|
||||
feats=np.stack([np.frombuffer(r[3],np.float32) for r in keep]).astype(np.float32)
|
||||
feats/=np.clip(np.linalg.norm(feats,axis=1,keepdims=True),1e-9,None)
|
||||
cpas=np.array([r[0] for r in keep]); by=defaultdict(list)
|
||||
for i,c in enumerate(cpas): by[c].append(i)
|
||||
by={c:np.array(v) for c,v in by.items() if len(v)>=3}; accts=list(by.keys())
|
||||
pw=np.array([len(by[c])*(len(by[c])-1)/2 for c in accts],float); pw/=pw.sum()
|
||||
rng=np.random.default_rng(SEED); M=100_000
|
||||
intra=np.empty(M,np.float32); ci=rng.choice(len(accts),M,p=pw)
|
||||
for t in range(M):
|
||||
a,b=rng.choice(by[accts[ci[t]]],2,replace=False); intra[t]=feats[a]@feats[b]
|
||||
inter=np.empty(M,np.float32)
|
||||
for t in range(M):
|
||||
i,j=rng.choice(len(accts),2,replace=False); inter[t]=feats[rng.choice(by[accts[i]])]@feats[rng.choice(by[accts[j]])]
|
||||
xs=np.linspace(0.3,1.0,10000); diff=gaussian_kde(intra)(xs)-gaussian_kde(inter)(xs)
|
||||
cr=[float(x) for x in xs[np.where(np.diff(np.sign(diff)))[0]] if 0.6<x<0.99]
|
||||
print(f' [{label}] crossover {[f"{x:.4f}" for x in cr]} (n={len(keep)}, accts>=3={len(accts)})')
|
||||
def percomp_bands(keep,label,M=500_000):
|
||||
feats=np.stack([np.frombuffer(r[3],np.float32) for r in keep]).astype(np.float32)
|
||||
feats/=np.clip(np.linalg.norm(feats,axis=1,keepdims=True),1e-9,None)
|
||||
dh=np.stack([np.frombuffer(r[4],np.uint8) for r in keep]); cpas=np.array([r[0] for r in keep])
|
||||
by=defaultdict(list)
|
||||
for i,c in enumerate(cpas): by[c].append(i)
|
||||
accts=[c for c,v in by.items() if len(v)>=1]; rng=np.random.default_rng(SEED)
|
||||
n=len(keep); ii=rng.integers(0,n,M*2); jj=rng.integers(0,n,M*2)
|
||||
keepm=cpas[ii]!=cpas[jj]; ii=ii[keepm][:M]; jj=jj[keepm][:M]
|
||||
cos=np.einsum('ij,ij->i',feats[ii],feats[jj]); d=POP[dh[ii]^dh[jj]].sum(1)
|
||||
hc=(cos>0.95)&(d<=5); mc=(cos>0.95)&(d>5)&(d<=15); hsc=(cos>0.95)&(d>15)
|
||||
un=(cos>0.837)&(cos<=0.95); lh=cos<=0.837
|
||||
print(f' [{label}] per-COMPARISON ICCR (M={len(ii)}): HC {hc.mean():.6f} MC {mc.mean():.6f} HSC {hsc.mean():.6f} UN {un.mean():.4f} LH {lh.mean():.4f}')
|
||||
def persig_perdoc_bands(keep,label):
|
||||
n=len(keep); feats=np.stack([np.frombuffer(r[3],np.float32) for r in keep]).astype(np.float32)
|
||||
feats/=np.clip(np.linalg.norm(feats,axis=1,keepdims=True),1e-9,None)
|
||||
dh=np.stack([np.frombuffer(r[4],np.uint8) for r in keep]); cpas=np.array([r[0] for r in keep]); docs=np.array([r[2] for r in keep])
|
||||
ci=defaultdict(list)
|
||||
for i,c in enumerate(cpas): ci[c].append(i)
|
||||
ci={c:np.array(v) for c,v in ci.items()}; ps={c:len(v)-1 for c,v in ci.items()}
|
||||
allidx=np.arange(n); rng=np.random.default_rng(SEED); mc=np.zeros(n,np.float32); md=np.full(n,64,np.int32)
|
||||
for si in range(n):
|
||||
p=ps[cpas[si]]
|
||||
if p<=0: continue
|
||||
same=ci[cpas[si]]; need=p; cand=[]; att=0
|
||||
while need>0 and att<10:
|
||||
dr=rng.choice(n,size=need*2,replace=True); ok=dr[~np.isin(dr,same)]; cand.extend(ok[:need].tolist()); need-=len(ok[:need]); att+=1
|
||||
cand=np.array(cand[:p],dtype=np.int64)
|
||||
mc[si]=(feats[cand]@feats[si]).max(); md[si]=int(POP[dh[cand]^dh[si]].sum(1).min())
|
||||
un=(mc>0.837)&(mc<=0.95); hsc=(mc>0.95)&(md>15)
|
||||
# per-doc: any signature in band
|
||||
dd=defaultdict(list)
|
||||
for i in range(n): dd[docs[i]].append(i)
|
||||
docs_un=np.mean([un[v].any() for v in dd.values()]); docs_hsc=np.mean([hsc[v].any() for v in dd.values()])
|
||||
print(f' [{label}] per-SIGNATURE ICCR: UN {un.mean():.4f} HSC {hsc.mean():.6f}')
|
||||
print(f' [{label}] per-REPORT ICCR: UN {docs_un:.4f} HSC {docs_hsc:.6f} (n_doc={len(dd)})')
|
||||
|
||||
rows=load()
|
||||
bcd_all=[r for r in rows if r[1] in BIG4 and r[1]!=FIRM_A]
|
||||
bcd_19=[r for r in bcd_all if 2013<=r[5]<=2019]
|
||||
print("=== ITEM 11: KDE crossover (verify corpus 0.837 / BCD-all 0.8489, then closed-world 2013-2019) ===")
|
||||
crossover(rows,'corpus-wide (verify ~0.8367)')
|
||||
crossover(bcd_all,'BCD-only ALL period (verify 0.8489)')
|
||||
crossover(bcd_19,'BCD 2013-2019 CLOSED-WORLD (NEW primary candidate)')
|
||||
print("\n=== ITEM 3: UN / HSC full ICCR on BCD 2013-2019 ===")
|
||||
percomp_bands(bcd_19,'BCD 2013-2019')
|
||||
persig_perdoc_bands(bcd_19,'BCD 2013-2019')
|
||||
print("\n=== ITEM 12: n reconciliation ===")
|
||||
print(f" BCD full-period (2013-2023) signatures = {len(bcd_all)} <- Script53 logged n=89,994")
|
||||
print(f" BCD 2013-2019 signatures = {len(bcd_19)} <- headline ICCR base (reproduces 0.0059)")
|
||||
@@ -0,0 +1,63 @@
|
||||
"""F5 robustness: firm+year fixed-effects logistic regression and leave-one-year-out.
|
||||
Complements the pool-size stratification and accountant-clustered bootstrap (Section IV-C).
|
||||
Uses numpy+scipy only (no statsmodels). Reproduces from signature_analysis.db.
|
||||
"""
|
||||
import sqlite3, numpy as np
|
||||
from scipy.optimize import minimize
|
||||
|
||||
DB = "/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db"
|
||||
BIG4 = ('勤業眾信聯合', '資誠聯合', '安侯建業聯合', '安永聯合')
|
||||
FM = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
|
||||
con = sqlite3.connect(DB); cur = con.cursor()
|
||||
cur.execute(f"""
|
||||
SELECT s.excel_firm, CAST(substr(s.year_month,1,4) AS INT) yr,
|
||||
(CASE WHEN s.max_similarity_to_same_accountant>0.95 AND s.min_dhash_independent<=5 THEN 1 ELSE 0 END) hc,
|
||||
p.psize
|
||||
FROM signatures s
|
||||
JOIN (SELECT accountant_id, COUNT(*) psize FROM signatures
|
||||
WHERE is_valid=1 AND excel_firm IN ({','.join('?'*4)})
|
||||
AND max_similarity_to_same_accountant IS NOT NULL AND min_dhash_independent IS NOT NULL
|
||||
GROUP BY accountant_id) p ON s.accountant_id=p.accountant_id
|
||||
WHERE s.is_valid=1 AND s.excel_firm IN ({','.join('?'*4)})
|
||||
AND s.max_similarity_to_same_accountant IS NOT NULL AND s.min_dhash_independent IS NOT NULL
|
||||
AND s.year_month GLOB '2[0-9][0-9][0-9][0-9][0-9]'
|
||||
""", BIG4 + BIG4)
|
||||
rows = cur.fetchall(); con.close()
|
||||
firm = np.array([FM[r[0]] for r in rows]); yr = np.array([r[1] for r in rows])
|
||||
hc = np.array([r[2] for r in rows], float); pool = np.array([r[3] for r in rows], float)
|
||||
n = len(hc); years = sorted(set(yr.tolist()))
|
||||
|
||||
# --- firm + year FE logistic (Firm A & first year = reference) ---
|
||||
cols = [np.ones(n)]; names = ['const']
|
||||
for f in ['B', 'C', 'D']:
|
||||
cols.append((firm == f).astype(float)); names.append(f'firm_{f}')
|
||||
for y in years[1:]:
|
||||
cols.append((yr == y).astype(float)); names.append(f'yr_{y}')
|
||||
lp = np.log(pool); lp = (lp - lp.mean()) / lp.std()
|
||||
cols.append(lp); names.append('logpool_z')
|
||||
X = np.column_stack(cols)
|
||||
|
||||
def nll(b):
|
||||
z = X @ b
|
||||
return -np.sum(hc * z - np.logaddexp(0, z)) + 1e-6 * np.sum(b * b)
|
||||
def grad(b):
|
||||
p = 1 / (1 + np.exp(-(X @ b)))
|
||||
return -X.T @ (hc - p) + 2e-6 * b
|
||||
b = minimize(nll, np.zeros(X.shape[1]), jac=grad, method='L-BFGS-B').x
|
||||
print("Firm+Year FE logistic (Firm A & first year = ref):")
|
||||
for nm, bi in zip(names, b):
|
||||
if nm.startswith('firm') or nm == 'logpool_z':
|
||||
print(f" {nm:11} coef={bi:7.3f} OR={np.exp(bi):.4f}")
|
||||
|
||||
# --- leave-one-year-out firm contrast ---
|
||||
grp = np.where(firm == 'A', 'A', 'BCD')
|
||||
def rate(mask, g):
|
||||
m = mask & (grp == g); return 100 * hc[m].mean()
|
||||
print("\nLeave-one-year-out (Firm A minus B/C/D HC gap):")
|
||||
gaps = []
|
||||
for y in years:
|
||||
keep = (yr != y); a = rate(keep, 'A'); bb = rate(keep, 'BCD'); gaps.append(a - bb)
|
||||
print(f" drop {y}: A={a:.1f}% BCD={bb:.1f}% gap={a-bb:.1f}pp")
|
||||
print(f" full-sample gap={rate(np.ones(n, bool),'A')-rate(np.ones(n, bool),'BCD'):.1f}pp; "
|
||||
f"LOYO range=[{min(gaps):.1f}, {max(gaps):.1f}]pp")
|
||||
@@ -0,0 +1,70 @@
|
||||
import sqlite3
|
||||
from collections import defaultdict, Counter
|
||||
import numpy as np
|
||||
DB='/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A='勤業眾信聯合'; BIG4=('勤業眾信聯合','安侯建業聯合','資誠聯合','安永聯合')
|
||||
ALIAS={'勤業眾信聯合':'A','安侯建業聯合':'B','資誠聯合':'C','安永聯合':'D'}
|
||||
SEED=42; POP=np.array([bin(i).count('1') for i in range(256)],dtype=np.uint8)
|
||||
def wilson(k,n,z=1.96):
|
||||
if n==0: return (None,None)
|
||||
p=k/n; d=1+z*z/n; c=(p+z*z/(2*n))/d; h=z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0,c-h),min(1,c+h))
|
||||
def load():
|
||||
c=sqlite3.connect(f'file:{DB}?mode=ro',uri=True); cur=c.cursor()
|
||||
cur.execute("""SELECT s.assigned_accountant,a.firm,s.source_pdf,s.feature_vector,s.dhash_vector,
|
||||
CAST(substr(s.year_month,1,4) AS INT) FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
r=cur.fetchall(); c.close(); return r
|
||||
def canonical_sampler(rng,n,n_pool,same_cpa,all_idx):
|
||||
need=n_pool; cand=[]; att=0
|
||||
while need>0 and att<10:
|
||||
draw=rng.choice(n,size=need*2,replace=True); ok=draw[~np.isin(draw,same_cpa)]
|
||||
cand.extend(ok[:need].tolist()); need-=len(ok[:need]); att+=1
|
||||
if need>0:
|
||||
pm=np.ones(n,bool); pm[same_cpa]=False
|
||||
cand.extend(rng.choice(all_idx[pm],size=need,replace=False).tolist())
|
||||
return np.array(cand[:n_pool],dtype=np.int64)
|
||||
def simulate(keep):
|
||||
n=len(keep); feats=np.stack([np.frombuffer(r[3],np.float32) for r in keep]).astype(np.float32)
|
||||
nr=np.linalg.norm(feats,axis=1,keepdims=True); nr[nr==0]=1; feats=feats/nr
|
||||
dh=np.stack([np.frombuffer(r[4],np.uint8) for r in keep]); cpas=np.array([r[0] for r in keep])
|
||||
cpa_idx=defaultdict(list)
|
||||
for i,c in enumerate(cpas): cpa_idx[c].append(i)
|
||||
cpa_idx={c:np.array(v) for c,v in cpa_idx.items()}; ps={c:len(v)-1 for c,v in cpa_idx.items()}
|
||||
all_idx=np.arange(n); rng=np.random.default_rng(SEED)
|
||||
mc=np.zeros(n,np.float32); md=np.full(n,64,np.int32)
|
||||
for si in range(n):
|
||||
p=ps[cpas[si]]
|
||||
if p<=0: continue
|
||||
cand=canonical_sampler(rng,n,p,cpa_idx[cpas[si]],all_idx)
|
||||
mc[si]=(feats[cand]@feats[si]).max(); md[si]=int(POP[dh[cand]^dh[si]].sum(axis=1).min())
|
||||
return mc,md
|
||||
def iccr(keep,label):
|
||||
mc,md=simulate(keep); n=len(keep)
|
||||
hc=(mc>0.95)&(md<=5); d2=(mc>0.95)&(md<=15)
|
||||
un=(mc>0.837)&(mc<=0.95); hsc=(mc>0.95)&(md>15)
|
||||
print(f"\n== {label} (n_sig={n:,}) ==")
|
||||
for nm,a in [('HC',hc),('HC+MC',d2),('UN-band',un),('HSC-band',hsc)]:
|
||||
k=int(a.sum()); lo,hi=wilson(k,n); print(f" ICCR per-sig {nm}: {k/n:.6f} ({k}/{n}) [{lo:.5f},{hi:.5f}]")
|
||||
def a_oos(rows,label):
|
||||
A=[r for r in rows if r[1]==FIRM_A]; BCD=[r for r in rows if r[1] in BIG4 and r[1]!=FIRM_A]
|
||||
bf=np.stack([np.frombuffer(r[3],np.float32) for r in BCD]).astype(np.float32)
|
||||
bn=np.linalg.norm(bf,axis=1,keepdims=True); bn[bn==0]=1; bf=bf/bn
|
||||
bdh=np.stack([np.frombuffer(r[4],np.uint8) for r in BCD]); nb=bf.shape[0]
|
||||
ac=defaultdict(list)
|
||||
for i,r in enumerate(A): ac[r[0]].append(i)
|
||||
ps={c:len(v)-1 for c,v in ac.items()}; rng=np.random.default_rng(SEED); hc=np.zeros(len(A),bool)
|
||||
for i,r in enumerate(A):
|
||||
p=ps[r[0]]
|
||||
if p<=0: continue
|
||||
cand=rng.choice(nb,size=p,replace=True); sf=np.frombuffer(r[3],np.float32).astype(np.float32); sf=sf/max(np.linalg.norm(sf),1e-9)
|
||||
mc=(bf[cand]@sf).max(); mdv=int(POP[bdh[cand]^np.frombuffer(r[4],np.uint8)].sum(axis=1).min())
|
||||
hc[i]=(mc>0.95)and(mdv<=5)
|
||||
k=int(hc.sum()); n=len(A); lo,hi=wilson(k,n)
|
||||
print(f"\n== Firm A OOS vs {label} BCD pool == per-sig HC: {k/n:.6f} ({k}/{n}) [{lo:.6f},{hi:.6f}]")
|
||||
rows=load()
|
||||
bcd_all=[r for r in rows if r[1] in BIG4 and r[1]!=FIRM_A]
|
||||
bcd_19=[r for r in bcd_all if 2013<=r[5]<=2019]
|
||||
iccr(bcd_19,'BCD 2013-2019 (verify per-sig HC~0.0059)')
|
||||
a_oos([r for r in rows if 2013<=r[5]<=2019],'2013-2019')
|
||||
a_oos(rows,'full-period')
|
||||
@@ -0,0 +1,84 @@
|
||||
"""Figure 3 (real data version): 2D density of the two measures over the five-region scheme.
|
||||
Replaces the earlier schematic with the actual distribution, with axis ticks and the rule cuts.
|
||||
Reproduces from signature_analysis.db; Big-4, is_valid=1, both measures present."""
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.colors import LogNorm
|
||||
from matplotlib.patches import Rectangle
|
||||
import numpy as np
|
||||
import sqlite3
|
||||
|
||||
DB = "/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db"
|
||||
BIG4 = ('勤業眾信聯合', '資誠聯合', '安侯建業聯合', '安永聯合')
|
||||
|
||||
con = sqlite3.connect(DB)
|
||||
cur = con.cursor()
|
||||
cur.execute(f"""
|
||||
SELECT s.max_similarity_to_same_accountant, s.min_dhash_independent
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.max_similarity_to_same_accountant IS NOT NULL
|
||||
AND s.min_dhash_independent IS NOT NULL
|
||||
AND a.firm IN ({','.join(['?']*4)})
|
||||
""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
con.close()
|
||||
cos = np.array([r[0] for r in rows], dtype=float)
|
||||
dh = np.array([r[1] for r in rows], dtype=float)
|
||||
n = len(cos)
|
||||
|
||||
LO, HI = 0.8547, 0.95
|
||||
DH1, DH2 = 5, 15
|
||||
xmin, xmax = 0.70, 1.002
|
||||
ymin, ymax = -0.5, 30
|
||||
ycap = 30 # display cap; values above are piled into the top row for visibility
|
||||
|
||||
dh_disp = np.minimum(dh, ycap - 0.5)
|
||||
|
||||
fig, ax = plt.subplots(figsize=(5.6, 4.4))
|
||||
|
||||
# faint region tint behind the density
|
||||
ax.add_patch(Rectangle((xmin, ymin), LO - xmin, ymax - ymin, facecolor='#bdc3c7', alpha=0.12, zorder=0))
|
||||
ax.add_patch(Rectangle((LO, ymin), HI - LO, ymax - ymin, facecolor='#f7dc6f', alpha=0.12, zorder=0))
|
||||
ax.add_patch(Rectangle((HI, ymin), xmax - HI, DH1 - ymin, facecolor='#cb4335', alpha=0.14, zorder=0))
|
||||
ax.add_patch(Rectangle((HI, DH1), xmax - HI, DH2 - DH1, facecolor='#eb984e', alpha=0.14, zorder=0))
|
||||
ax.add_patch(Rectangle((HI, DH2), xmax - HI, ymax - DH2, facecolor='#aed6f1', alpha=0.14, zorder=0))
|
||||
|
||||
# real 2D density (log counts)
|
||||
xedges = np.linspace(xmin, xmax, 90)
|
||||
yedges = np.arange(-0.5, ycap + 0.5, 1.0) # integer dHash bins
|
||||
H, xe, ye = np.histogram2d(cos, dh_disp, bins=[xedges, yedges])
|
||||
pcm = ax.pcolormesh(xe, ye, H.T, norm=LogNorm(vmin=1, vmax=H.max()),
|
||||
cmap='viridis', zorder=1, shading='flat')
|
||||
cb = fig.colorbar(pcm, ax=ax, pad=0.02)
|
||||
cb.set_label('signatures per cell (log scale)', fontsize=8)
|
||||
cb.ax.tick_params(labelsize=7)
|
||||
|
||||
# cut lines
|
||||
ax.axvline(LO, color='gray', ls=':', lw=1.1, zorder=3)
|
||||
ax.axvline(HI, color='black', ls='--', lw=1.1, zorder=3)
|
||||
ax.plot([HI, xmax], [DH1, DH1], 'k--', lw=0.9, zorder=3)
|
||||
ax.plot([HI, xmax], [DH2, DH2], 'k--', lw=0.9, zorder=3)
|
||||
|
||||
# region labels
|
||||
ax.text((xmin + LO) / 2, 24, 'LH', ha='center', fontsize=10, weight='bold', color='#34495e', zorder=4)
|
||||
ax.text((LO + HI) / 2, 24, 'UN', ha='center', fontsize=10, weight='bold', color='#7d6608', zorder=4)
|
||||
ax.text((HI + xmax) / 2, 2.2, 'HC', ha='center', fontsize=10, weight='bold', color='#cb4335', zorder=4)
|
||||
ax.text((HI + xmax) / 2, 9.7, 'MC', ha='center', fontsize=10, weight='bold', color='#a04000', zorder=4)
|
||||
ax.text((HI + xmax) / 2, 24, 'HSC', ha='center', fontsize=9, weight='bold', color='#21618c', zorder=4)
|
||||
|
||||
ax.set_xlim(xmin, xmax)
|
||||
ax.set_ylim(ymin, ymax)
|
||||
ax.set_xticks([0.70, 0.75, 0.80, 0.8547, 0.90, 0.95, 1.00])
|
||||
ax.set_xticklabels(['0.70', '0.75', '0.80', '0.855', '0.90', '0.95', '1.00'], fontsize=7.5)
|
||||
ax.set_yticks([0, 5, 10, 15, 20, 25, 30])
|
||||
ax.set_yticklabels(['0', '5', '10', '15', '20', '25', '≥30'], fontsize=7.5)
|
||||
ax.set_xlabel('cosine similarity to same accountant (style)', fontsize=9)
|
||||
ax.set_ylabel('min dHash distance (structure)', fontsize=9)
|
||||
ax.set_title(f'Figure 3. Two-measure plane: real density over the five regions (Big-4, n={n:,})',
|
||||
fontsize=8.5)
|
||||
fig.tight_layout()
|
||||
out = '/Volumes/NV2/pdf_recognize/paper/v13_build/figures/fig3.png'
|
||||
fig.savefig(out, dpi=200, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
print(f'fig3 density OK: n={n:,}, dHash>=30 piled: {(dh>=ycap).sum()}, written {out}')
|
||||
@@ -0,0 +1,62 @@
|
||||
"""Figure 6: two-measure sensitivity surface over the (cosine cut x dHash cut) plane.
|
||||
Panel A: clean-group (B/C/D) flag rate -- how permissive the operating point is.
|
||||
Panel B: Firm A minus B/C/D flag-rate contrast (pp) -- discrimination across the plane.
|
||||
Shows the chosen HC point (0.95, dHash<=5) is not a cherry-picked threshold and exposes
|
||||
the weaker MC band (dHash<=15). Reproduces from signature_analysis.db (DB columns only).
|
||||
"""
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import sqlite3
|
||||
|
||||
DB = "/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db"
|
||||
BIG4 = ('勤業眾信聯合', '資誠聯合', '安侯建業聯合', '安永聯合')
|
||||
|
||||
con = sqlite3.connect(DB); cur = con.cursor()
|
||||
cur.execute(f"""SELECT CASE WHEN a.firm='勤業眾信聯合' THEN 1 ELSE 0 END isA,
|
||||
s.max_similarity_to_same_accountant c, s.min_dhash_independent d
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE a.firm IN ({','.join('?'*4)})
|
||||
AND s.max_similarity_to_same_accountant IS NOT NULL AND s.min_dhash_independent IS NOT NULL""", BIG4)
|
||||
rows = cur.fetchall(); con.close()
|
||||
isA = np.array([r[0] for r in rows], bool)
|
||||
c = np.array([r[1] for r in rows]); d = np.array([r[2] for r in rows])
|
||||
cA, dA = c[isA], d[isA]; cB, dB = c[~isA], d[~isA]
|
||||
|
||||
cos_cuts = np.arange(0.85, 0.9901, 0.0025)
|
||||
dh_cuts = np.arange(0, 21, 1)
|
||||
A = np.zeros((len(dh_cuts), len(cos_cuts)))
|
||||
B = np.zeros_like(A)
|
||||
for j, cc in enumerate(cos_cuts):
|
||||
for i, dd in enumerate(dh_cuts):
|
||||
A[i, j] = 100 * np.mean((cA > cc) & (dA <= dd))
|
||||
B[i, j] = 100 * np.mean((cB > cc) & (dB <= dd))
|
||||
contrast = A - B
|
||||
|
||||
extent = [cos_cuts[0], cos_cuts[-1], dh_cuts[0], dh_cuts[-1]]
|
||||
fig, axes = plt.subplots(1, 2, figsize=(10.5, 4.3))
|
||||
|
||||
for ax, Z, title, cmap, lab in [
|
||||
(axes[0], B, '(a) Clean group (B/C/D) flag rate', 'viridis', 'flag rate (%)'),
|
||||
(axes[1], contrast, '(b) Firm A − B/C/D contrast', 'magma', 'contrast (pp)')]:
|
||||
im = ax.imshow(Z, origin='lower', aspect='auto', extent=extent, cmap=cmap)
|
||||
cb = fig.colorbar(im, ax=ax, pad=0.02); cb.set_label(lab, fontsize=8); cb.ax.tick_params(labelsize=7)
|
||||
# operating points
|
||||
ax.scatter([0.95], [5], marker='*', s=180, color='white', edgecolor='black', zorder=5,
|
||||
label='HC operating point (0.95, dHash≤5)')
|
||||
ax.axhline(15, color='white', ls=':', lw=1.0)
|
||||
ax.text(0.853, 15.4, 'MC upper bound (dHash≤15)', color='white', fontsize=6.5, va='bottom')
|
||||
ax.set_xlabel('cosine cut', fontsize=9)
|
||||
ax.set_ylabel('dHash cut (≤)', fontsize=9)
|
||||
ax.set_title(title, fontsize=9)
|
||||
ax.tick_params(labelsize=7.5)
|
||||
ax.legend(loc='lower left', fontsize=6.5, framealpha=0.85)
|
||||
|
||||
fig.suptitle('Figure 6. Sensitivity surface of the deployed rule over the two-measure threshold plane (Big-4, n=%d).' % len(c),
|
||||
fontsize=9, y=1.02)
|
||||
fig.tight_layout()
|
||||
out = '/Volumes/NV2/pdf_recognize/paper/v13_build/figures/fig6.png'
|
||||
fig.savefig(out, dpi=200, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
print(f"fig6 OK n={len(c)}; HC(0.95,5) contrast={contrast[5, np.argmin(abs(cos_cuts-0.95))]:.1f}pp; written {out}")
|
||||
@@ -0,0 +1,58 @@
|
||||
import sqlite3, numpy as np
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
DB='/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
ALIAS={'勤業眾信聯合':'A','安侯建業聯合':'B','資誠聯合':'C','安永聯合':'D'}
|
||||
COL={'A':'#c0392b','B':'#2980b9','C':'#27ae60','D':'#8e44ad'}
|
||||
c=sqlite3.connect(f'file:{DB}?mode=ro',uri=True)
|
||||
rows=c.execute("""SELECT a.firm, s.max_similarity_to_same_accountant, s.min_dhash_independent,
|
||||
s.assigned_accountant, CAST(substr(s.year_month,1,4) AS INT)
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.max_similarity_to_same_accountant IS NOT NULL AND s.min_dhash_independent IS NOT NULL
|
||||
AND a.firm IN ('勤業眾信聯合','安侯建業聯合','資誠聯合','安永聯合')""").fetchall()
|
||||
firm=np.array([ALIAS[r[0]] for r in rows]); cos=np.array([r[1] for r in rows],float)
|
||||
dh=np.array([r[2] for r in rows],float); acc=np.array([r[3] for r in rows]); yr=np.array([r[4] for r in rows])
|
||||
A=firm=='A'; BCD=np.isin(firm,['B','C','D'])
|
||||
|
||||
# ---- Figure 4: two panels, Firm A vs BCD ----
|
||||
fig,ax=plt.subplots(1,2,figsize=(9,3.4))
|
||||
ax[0].hist(cos[A],bins=np.linspace(0.7,1.0,60),density=True,alpha=0.6,color='#c0392b',label='Firm A')
|
||||
ax[0].hist(cos[BCD],bins=np.linspace(0.7,1.0,60),density=True,alpha=0.5,color='#34495e',label='Firms B/C/D')
|
||||
ax[0].axvline(0.95,ls='--',c='k',lw=0.8); ax[0].axvline(0.8547,ls=':',c='gray',lw=0.8)
|
||||
ax[0].set_title('(a) Within-accountant cosine',fontsize=10)
|
||||
ax[0].set_xlabel('max cosine to same accountant'); ax[0].set_ylabel('density')
|
||||
ax[0].text(0.952,ax[0].get_ylim()[1]*0.9,'0.95',fontsize=7); ax[0].legend(fontsize=8,frameon=False)
|
||||
ax[0].annotate('A median 0.986',(0.986,0),(0.80,ax[0].get_ylim()[1]*0.55),fontsize=7,color='#c0392b',arrowprops=dict(arrowstyle='->',color='#c0392b',lw=0.7))
|
||||
ax[0].annotate('B/C/D median 0.959',(0.959,0),(0.72,ax[0].get_ylim()[1]*0.35),fontsize=7,color='#34495e',arrowprops=dict(arrowstyle='->',color='#34495e',lw=0.7))
|
||||
bins=np.arange(0,21)-0.5
|
||||
ax[1].hist(np.clip(dh[A],0,20),bins=bins,density=True,alpha=0.6,color='#c0392b',label='Firm A')
|
||||
ax[1].hist(np.clip(dh[BCD],0,20),bins=bins,density=True,alpha=0.5,color='#34495e',label='Firms B/C/D')
|
||||
ax[1].axvline(5,ls='--',c='k',lw=0.8)
|
||||
ax[1].set_title('(b) Within-accountant dHash',fontsize=10)
|
||||
ax[1].set_xlabel('min dHash to same accountant'); ax[1].set_ylabel('density')
|
||||
ax[1].text(5.1,ax[1].get_ylim()[1]*0.9,'5',fontsize=7); ax[1].legend(fontsize=8,frameon=False)
|
||||
ax[1].text(0.50,0.62,'A median 2 / B,C,D median 7',transform=ax[1].transAxes,fontsize=7)
|
||||
fig.text(0.5,-0.02,'Cross-firm held-out HC rate 0.42% sits at/below the clean reference ICCR 0.59%; within-Firm-A HC rate is 82%.',ha='center',fontsize=7,style='italic')
|
||||
fig.tight_layout(); fig.savefig('/tmp/fig4.png',dpi=200,bbox_inches='tight'); plt.close(fig)
|
||||
|
||||
# ---- Figure 5: per-accountant HC rate, ranked, per period ----
|
||||
def hc_by_acc(mask):
|
||||
out={}
|
||||
a=acc[mask]; h=((cos[mask]>0.95)&(dh[mask]<=5)).astype(float); f=firm[mask]
|
||||
for ai in np.unique(a):
|
||||
m=a==ai
|
||||
if m.sum()>=5: out[ai]=(h[m].mean(),f[m][0])
|
||||
return out
|
||||
fig,ax=plt.subplots(1,2,figsize=(9,3.4),sharey=True)
|
||||
for j,(lo,hi,ttl) in enumerate([(2013,2019,'(a) 2013–2019'),(2020,2023,'(b) 2020–2023')]):
|
||||
d=hc_by_acc(BCD|A if False else ((yr>=lo)&(yr<=hi)))
|
||||
items=sorted(d.items(),key=lambda kv:-kv[1][0])
|
||||
xs=np.arange(len(items)); ys=[v[0]*100 for _,v in items]; cs=[COL[v[1]] for _,v in items]
|
||||
ax[j].scatter(xs,ys,c=cs,s=10)
|
||||
ax[j].set_title(ttl,fontsize=10); ax[j].set_xlabel('accountant rank');
|
||||
if j==0: ax[j].set_ylabel('per-accountant HC rate (%)')
|
||||
from matplotlib.lines import Line2D
|
||||
ax[1].legend([Line2D([0],[0],marker='o',ls='',color=COL[k]) for k in 'ABCD'],['Firm A','Firm B','Firm C','Firm D'],fontsize=7,frameon=False,loc='upper right')
|
||||
fig.tight_layout(); fig.savefig('/tmp/fig5.png',dpi=200,bbox_inches='tight'); plt.close(fig)
|
||||
print('figs OK', __import__('os').path.getsize('/tmp/fig4.png'), __import__('os').path.getsize('/tmp/fig5.png'))
|
||||
@@ -0,0 +1,75 @@
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch, Rectangle
|
||||
import numpy as np
|
||||
|
||||
# ============ Figure 1: data split grid ============
|
||||
fig, ax = plt.subplots(figsize=(7, 3.2))
|
||||
firms = ['Firm A', 'Firm B', 'Firm C', 'Firm D']
|
||||
periods = ['2013–2019', '2020–2023']
|
||||
# role per (row firm, col period)
|
||||
def role(f, p):
|
||||
if f == 'Firm A':
|
||||
return ('Held-out test 1\n(Firm A, full record)', '#c0392b')
|
||||
if p == '2013–2019':
|
||||
return ('Calibration\n(clean reference)', '#27ae60')
|
||||
return ('Held-out test 2\n(secondary)', '#2980b9')
|
||||
for i, f in enumerate(firms):
|
||||
for j, p in enumerate(periods):
|
||||
txt, col = role(f, p)
|
||||
ax.add_patch(Rectangle((j, len(firms)-1-i), 1, 1, facecolor=col, alpha=0.30, edgecolor='black', lw=1))
|
||||
ax.text(j+0.5, len(firms)-1-i+0.5, txt, ha='center', va='center', fontsize=6.5)
|
||||
ax.set_xlim(0, 2); ax.set_ylim(0, 4)
|
||||
ax.set_xticks([0.5, 1.5]); ax.set_xticklabels(periods, fontsize=9)
|
||||
ax.set_yticks([3.5, 2.5, 1.5, 0.5]); ax.set_yticklabels(firms, fontsize=9)
|
||||
ax.tick_params(length=0)
|
||||
for s in ax.spines.values(): s.set_visible(False)
|
||||
ax.set_title('Figure 1. Data split: calibrate on the clean cell, test everything else', fontsize=9)
|
||||
fig.tight_layout(); fig.savefig('/tmp/fig1.png', dpi=200, bbox_inches='tight'); plt.close(fig)
|
||||
|
||||
# ============ Figure 2: pipeline ============
|
||||
fig, ax = plt.subplots(figsize=(9, 2.5))
|
||||
steps = ['Raw PDF\nreport', 'Find signature\npage (VLM)', 'Detect signatures\n(YOLOv11)\n+ red-stamp removal',
|
||||
'Feature extraction\n(ResNet-50, 2048-d)', 'Two similarities\ncosine (style)\nmin dHash (structure)', 'Five-way\nlabel']
|
||||
n = len(steps); w = 1.0/n
|
||||
cols = ['#ecf0f1', '#d6eaf8', '#d5f5e3', '#fcf3cf', '#fadbd8', '#e8daef']
|
||||
for i, (s, c) in enumerate(zip(steps, cols)):
|
||||
x = i*w + 0.01
|
||||
ax.add_patch(FancyBboxPatch((x, 0.30), w-0.02, 0.40, boxstyle='round,pad=0.005,rounding_size=0.02',
|
||||
facecolor=c, edgecolor='black', lw=1, transform=ax.transAxes))
|
||||
ax.text(x+(w-0.02)/2, 0.50, s, ha='center', va='center', fontsize=6.8, transform=ax.transAxes)
|
||||
if i < n-1:
|
||||
ax.add_patch(FancyArrowPatch((x+w-0.012, 0.50), (x+w+0.002, 0.50), transform=ax.transAxes,
|
||||
arrowstyle='-|>', mutation_scale=10, lw=1.2, color='black'))
|
||||
ax.axis('off')
|
||||
ax.set_title('Figure 2. The screening pipeline', fontsize=9, y=0.92)
|
||||
fig.savefig('/tmp/fig2.png', dpi=200, bbox_inches='tight'); plt.close(fig)
|
||||
|
||||
# ============ Figure 3: two-measure plane, five regions ============
|
||||
fig, ax = plt.subplots(figsize=(5.2, 4.2))
|
||||
LO, HI = 0.8547, 0.95
|
||||
DH1, DH2 = 5, 15
|
||||
xmin, xmax = 0.70, 1.005
|
||||
ymin, ymax = -1, 30
|
||||
# LH (cos<=LO): whole column
|
||||
ax.add_patch(Rectangle((xmin, ymin), LO-xmin, ymax-ymin, facecolor='#bdc3c7', alpha=0.5))
|
||||
# UN (LO<cos<=HI)
|
||||
ax.add_patch(Rectangle((LO, ymin), HI-LO, ymax-ymin, facecolor='#f7dc6f', alpha=0.5))
|
||||
# high-cosine band subdivided by dHash
|
||||
ax.add_patch(Rectangle((HI, ymin), xmax-HI, DH1-ymin, facecolor='#cb4335', alpha=0.55)) # HC dHash<=5
|
||||
ax.add_patch(Rectangle((HI, DH1), xmax-HI, DH2-DH1, facecolor='#eb984e', alpha=0.55)) # MC 5<dHash<=15
|
||||
ax.add_patch(Rectangle((HI, DH2), xmax-HI, ymax-DH2, facecolor='#aed6f1', alpha=0.6)) # HSC dHash>15
|
||||
ax.axvline(LO, color='gray', ls=':', lw=1); ax.axvline(HI, color='black', ls='--', lw=1)
|
||||
ax.plot([HI, xmax], [DH1, DH1], 'k--', lw=0.8); ax.plot([HI, xmax], [DH2, DH2], 'k--', lw=0.8)
|
||||
ax.text((xmin+LO)/2, 22, 'LH', ha='center', fontsize=11, weight='bold')
|
||||
ax.text((LO+HI)/2, 22, 'UN', ha='center', fontsize=11, weight='bold')
|
||||
ax.text((HI+xmax)/2, 2, 'HC', ha='center', fontsize=11, weight='bold', color='white')
|
||||
ax.text((HI+xmax)/2, 9.5, 'MC', ha='center', fontsize=11, weight='bold')
|
||||
ax.text((HI+xmax)/2, 22, 'HSC', ha='center', fontsize=10, weight='bold')
|
||||
ax.text(LO, ymin-1.5, '0.8547', ha='center', fontsize=7); ax.text(HI, ymin-1.5, '0.95', ha='center', fontsize=7)
|
||||
ax.set_xlim(xmin, xmax); ax.set_ylim(ymin, ymax)
|
||||
ax.set_xlabel('cosine similarity (style)'); ax.set_ylabel('dHash distance (structure)')
|
||||
ax.set_title('Figure 3. The two measures and the five regions', fontsize=9)
|
||||
fig.tight_layout(); fig.savefig('/tmp/fig3.png', dpi=200, bbox_inches='tight'); plt.close(fig)
|
||||
print('figs 1/2/3 OK')
|
||||
@@ -0,0 +1,63 @@
|
||||
"""Imaging-pipeline audit (Table V) + byte-identity era split (Section V-B).
|
||||
Classifies a stratified sample of report PDFs as scanned / OCR'd / digital-native
|
||||
from embedded metadata + extractable-text heuristic, and tabulates by year and firm.
|
||||
Also reports the scan-era vs digital-era split of the 262 byte-identical signatures.
|
||||
|
||||
Requires: PyMuPDF (fitz); signature_analysis.db; original PDFs under total-pdf/.
|
||||
"""
|
||||
import fitz, os, glob, sqlite3
|
||||
from collections import defaultdict
|
||||
|
||||
fitz.TOOLS.mupdf_display_errors(False)
|
||||
DB = "/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db"
|
||||
PDF_ROOT = "/Volumes/NV2/PDF-Processing/total-pdf"
|
||||
BIG4 = ('勤業眾信聯合', '資誠聯合', '安侯建業聯合', '安永聯合')
|
||||
FMAP = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
|
||||
con = sqlite3.connect(DB); cur = con.cursor()
|
||||
|
||||
# --- stratified sample: 20 distinct PDFs per firm-year ---
|
||||
cur.execute(f"""
|
||||
WITH d AS (SELECT DISTINCT excel_firm, substr(year_month,1,4) yr, source_pdf,
|
||||
ROW_NUMBER() OVER (PARTITION BY excel_firm, substr(year_month,1,4) ORDER BY source_pdf) rn
|
||||
FROM signatures WHERE excel_firm IN ({','.join(['?']*4)}) AND source_pdf IS NOT NULL)
|
||||
SELECT excel_firm, yr, source_pdf FROM d WHERE rn<=20 ORDER BY yr""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
idx = {os.path.basename(p): p for p in glob.glob(PDF_ROOT + '/*/*.pdf')}
|
||||
|
||||
def classify(path):
|
||||
try:
|
||||
doc = fitz.open(path)
|
||||
except Exception:
|
||||
return None
|
||||
text = sum(len(doc[i].get_text().strip()) for i in range(min(len(doc), 4)))
|
||||
doc.close()
|
||||
return 'DIGITAL' if text > 2000 else ('OCR' if text > 200 else 'SCAN')
|
||||
|
||||
byyear = defaultdict(lambda: defaultdict(int))
|
||||
for firm, yr, fn in rows:
|
||||
p = idx.get(fn)
|
||||
if not p:
|
||||
continue
|
||||
k = classify(p)
|
||||
if k:
|
||||
byyear[yr][k] += 1
|
||||
|
||||
print("year | n | scan% | ocr% | digital%")
|
||||
for yr in sorted(byyear):
|
||||
d = byyear[yr]; n = sum(d.values())
|
||||
print(f"{yr} | {n} | {100*d['SCAN']//n} | {100*d['OCR']//n} | {100*d['DIGITAL']//n}")
|
||||
|
||||
# --- byte-identity era split ---
|
||||
cur.execute(f"""
|
||||
SELECT CASE WHEN year_month<'202101' THEN 'scan-era' ELSE 'digital-era' END era,
|
||||
CASE excel_firm WHEN '勤業眾信聯合' THEN 'A' WHEN '安侯建業聯合' THEN 'B'
|
||||
WHEN '資誠聯合' THEN 'C' WHEN '安永聯合' THEN 'D' END firm,
|
||||
COUNT(*) n
|
||||
FROM signatures WHERE is_valid=1 AND pixel_identical_to_closest=1
|
||||
AND excel_firm IN ({','.join(['?']*4)})
|
||||
GROUP BY era, firm ORDER BY era, firm""", BIG4)
|
||||
print("\nbyte-identical by era x firm:")
|
||||
for era, firm, n in cur.fetchall():
|
||||
print(f" {era} | {firm} | {n}")
|
||||
con.close()
|
||||
@@ -0,0 +1,28 @@
|
||||
"""Table VI: HC flag rate by firm under any-pair (deployed) vs strict same-pair rule.
|
||||
same-pair dHash = Hamming distance between a signature's dHash and its cosine-closest
|
||||
same-accountant partner (closest_match_file). Reproduces from signature_analysis.db."""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
DB="/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db"
|
||||
BIG4=('勤業眾信聯合','資誠聯合','安侯建業聯合','安永聯合')
|
||||
FM={'勤業眾信聯合':'A','安侯建業聯合':'B','資誠聯合':'C','安永聯合':'D'}
|
||||
con=sqlite3.connect(DB);cur=con.cursor()
|
||||
cur.execute("SELECT image_filename, dhash_vector FROM signatures WHERE dhash_vector IS NOT NULL")
|
||||
dh={fn:bytes(b) for fn,b in cur.fetchall()}
|
||||
ham=lambda a,b: bin(int.from_bytes(a,'big')^int.from_bytes(b,'big')).count('1')
|
||||
cur.execute(f"""SELECT excel_firm,max_similarity_to_same_accountant,min_dhash_independent,closest_match_file,image_filename
|
||||
FROM signatures WHERE is_valid=1 AND max_similarity_to_same_accountant IS NOT NULL
|
||||
AND min_dhash_independent IS NOT NULL AND excel_firm IN ({','.join('?'*4)})""",BIG4)
|
||||
st=defaultdict(lambda:[0,0,0])
|
||||
for firm,cos,mindh,cmf,imf in cur.fetchall():
|
||||
f=FM[firm]; st[f][0]+=1
|
||||
st[f][1]+= (cos>0.95 and mindh<=5)
|
||||
sp=ham(dh[imf],dh[cmf]) if (cmf in dh and imf in dh) else 99
|
||||
st[f][2]+= (cos>0.95 and sp<=5)
|
||||
con.close()
|
||||
print(f"{'firm':5}{'n':>8}{'any-pair%':>11}{'same-pair%':>12}")
|
||||
T=[0,0,0]
|
||||
for f in 'ABCD':
|
||||
n,a,s=st[f]; T=[T[0]+n,T[1]+a,T[2]+s]
|
||||
print(f"{f:5}{n:>8}{100*a/n:>10.1f}%{100*s/n:>11.1f}%")
|
||||
n,a,s=T; print(f"{'all':5}{n:>8}{100*a/n:>10.1f}%{100*s/n:>11.1f}%")
|
||||
@@ -0,0 +1,49 @@
|
||||
import sqlite3, numpy as np
|
||||
DB='/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
BCD=('安侯建業聯合','資誠聯合','安永聯合')
|
||||
c=sqlite3.connect(f'file:{DB}?mode=ro',uri=True)
|
||||
rows=c.execute("""SELECT s.assigned_accountant, s.max_similarity_to_same_accountant, s.min_dhash_independent
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE a.firm IN ('安侯建業聯合','資誠聯合','安永聯合')
|
||||
AND CAST(substr(s.year_month,1,4) AS INT) BETWEEN 2013 AND 2019
|
||||
AND s.max_similarity_to_same_accountant IS NOT NULL AND s.min_dhash_independent IS NOT NULL""").fetchall()
|
||||
from collections import defaultdict
|
||||
by=defaultdict(list)
|
||||
for a,cos,dh in rows: by[a].append((cos,dh))
|
||||
accs={a:np.array(v) for a,v in by.items() if len(v)>=15}
|
||||
print(f"BCD 2013-2019: {len(accs)} accountants with >=15 signatures (of {len(by)} total)")
|
||||
|
||||
rep=[]; tight=[]; rem_med=[]; klass=[]
|
||||
for a,v in accs.items():
|
||||
cos=v[:,0]; dh=v[:,1]
|
||||
hc=(cos>0.95)&(dh<=5)
|
||||
rf=hc.mean(); tf=(cos>0.95).mean()
|
||||
isolated=cos[cos<=0.95]
|
||||
rm=np.median(isolated) if len(isolated)>=3 else np.nan
|
||||
rep.append(rf); tight.append(tf); rem_med.append(rm)
|
||||
klass.append('pure-hand' if rf<0.10 else ('pure-stamp' if rf>0.90 else 'mixed'))
|
||||
rep=np.array(rep); tight=np.array(tight); rem_med=np.array(rem_med); klass=np.array(klass)
|
||||
|
||||
import collections
|
||||
print("\n=== Per-accountant replication-fraction (HC share) distribution ===")
|
||||
for lo,hi in [(0,0.1),(0.1,0.3),(0.3,0.5),(0.5,0.7),(0.7,0.9),(0.9,1.01)]:
|
||||
n=((rep>=lo)&(rep<hi)).sum(); print(f" rep_frac [{lo:.1f},{hi:.1f}): {n:3d} accountants")
|
||||
print(" class counts:", dict(collections.Counter(klass)))
|
||||
|
||||
mixed=klass=='mixed'
|
||||
print(f"\n=== MIXED accountants (n={mixed.sum()}): is the non-tight remainder dispersed (separable)? ===")
|
||||
rm_mixed=rem_med[mixed & ~np.isnan(rem_med)]
|
||||
print(f" remainder (cos<=0.95) median cosine across mixed accountants: median={np.median(rm_mixed):.3f}, IQR[{np.percentile(rm_mixed,25):.3f},{np.percentile(rm_mixed,75):.3f}]")
|
||||
print(f" fraction of mixed accountants whose remainder median < 0.90 (clearly dispersed): {(rm_mixed<0.90).mean():.2f}")
|
||||
print(f" fraction with remainder median < 0.85 (very dispersed): {(rm_mixed<0.85).mean():.2f}")
|
||||
# gap between tight group (cos>0.95) and remainder: per mixed accountant
|
||||
gaps=[]
|
||||
for a,v in accs.items():
|
||||
cos=v[:,0]
|
||||
t=cos[cos>0.95]; r=cos[cos<=0.95]
|
||||
if len(t)>=3 and len(r)>=3:
|
||||
gaps.append(np.median(t)-np.median(r))
|
||||
gaps=np.array(gaps)
|
||||
print(f"\n=== Tight-vs-remainder cosine gap (all accountants with both parts, n={len(gaps)}) ===")
|
||||
print(f" median gap = {np.median(gaps):.3f} (large gap => two-component structure is real & separable)")
|
||||
print(f" fraction with gap > 0.10: {(gaps>0.10).mean():.2f}")
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,38 @@
|
||||
# Paper A v13 修訂摘要(給 Jimmy)— 2026-06-23
|
||||
|
||||
**現行版本**:rev9.1(`Paper_full_v13_filled_rev9_20260623.docx` / PDF 同名)。源檔 `paper/v13_build/paper_v13_filled.md`,已 commit+push 至 `paper-a-v4-big4`。
|
||||
|
||||
**一句話**:用三份 AI 審稿(Gemini 3.1 + ChatGPT 5.5 + Opus 4.8 融合,共 29 點;另加兩輪 ChatGPT 5.5 敵意審稿)逐點修訂。所有新數字皆由資料庫實算、可重現,**未杜撰任何數據**。
|
||||
|
||||
---
|
||||
|
||||
## 一、核心宣稱「誠實化」(最重要,請確認你能接受降溫幅度)
|
||||
|
||||
- **撤回「139×」式比較**。原本「Firm A 觸發率 = 巧合率的 139 倍」是把「同一人重複」除以「不同人互撞」,方向高估。改為只報原始比率(A 82% vs B/C/D 24–35%),不再乘除。
|
||||
- **specificity → specificity proxy**:我們沒有 labeled negatives,明說 ICCR 是「不同會計師間的巧合率」,**不是**真正的偽陽率、連上下界都不是。
|
||||
- **新增 §III-F「What HC Means and Does Not Mean」**:白紙黑字寫「**HC 不是 reuse 標籤**」——HC 只代表「同一會計師極端重複、且在不相關會計師間罕見」。reuse 是其中一種詮釋,Firm A 由 byte-identical + 訪談另外支撐;**B/C/D 完全不作 reuse 宣稱**。
|
||||
- **Firm A 改稱 known-positive / quasi-positive benchmark**,不再包裝成嚴格 blinded test(因為訪談早已知道它是 stamping firm)。
|
||||
- **訪談降為 contextual / corroborative**,明說「非 validation、不可獨立重製」。
|
||||
|
||||
## 二、新增的實證強化(讓宣稱更站得住)
|
||||
|
||||
- **Table VI(any-pair vs same-pair)**:審稿質疑「cosine 與 dHash 來自不同配對、HC 是拼湊的」。實算反駁——改用嚴格 same-pair(同一配對需同時滿足兩條件),Firm A 仍 **57.3%**、B/C/D 跌到 5–9%,**A/其他的比值反而從 2.4–3.4× 升到 6.4–10.8×**。
|
||||
- **F5 穩健性四連檢**(§IV-C):pool-size 分層、會計師層 bootstrap(差距 53.7pp [49.5, 57.5])、firm+year 固定效果、逐年剔除(53.1–54.9pp)——firm 差異穿透所有控制。
|
||||
- **Table V — 影像管線審計(880 份 PDF)**:純掃描比例 2013 **82%** → 2021 **崩到 1%**,metadata 直接點名掃描機型(Fuji Xerox D125 等)。同時:(a) 坐實「事務所=影像管線」混淆是真的;(b) 但 Firm A 在純掃描年代就已高 HC,反證其訊號**不是**數位化 artifact。
|
||||
- **byte-identical 分期**:262 筆中 30 筆在掃描年代(18 在 Firm A,掃描雜訊無法偽造 → 重用鐵證)、232 筆在數位年代(誠實標註此暴增含「可偵測性」成分)。
|
||||
- 其餘:Figure 3 換真實密度圖、新增 Figure 6 閾值敏感度面、G5 補「全庫期望巧合 HC ≈ 888 件」。
|
||||
|
||||
## 三、需要你知道/拍板的三個判斷
|
||||
|
||||
1. **Framing 採「rebalance 不 relabel」**:審稿建議把全文改寫成「無標籤校準方法(審計只是 case study)」。我**沒有照做最強版**——把方法升為主貢獻、但**保留審計發現當 headline**,且**不宣稱 "general framework"**(避免「一個案例憑什麼叫 general」的新攻擊)。請確認你同意這個定位。
|
||||
2. **不做 within-CPA「真親簽」對照組**:理想上該比「真人親簽的 HC 誤觸率」,但我們沒有標記的親簽資料、公開資料集又是不同族群——硬借會引入新假設。已在 §III-E 主動說明「考慮過、刻意不做」。
|
||||
3. **PDF 管線發現是雙刃**:它讓「事務所混淆」更嚴重(我們誠實寫出),但同時用掃描年代證據鞏固核心。兩面都寫了,沒挑對我們有利的講。
|
||||
|
||||
## 四、剩餘待辦(你/投稿前)
|
||||
|
||||
- 作者、機構、DOI、biography placeholder(double-blind,投稿前補)
|
||||
- IEEE 模板最終排版;related work 可再收斂;表格 II-b/IV 是否整併
|
||||
- 人工 review protocol 首次執行(未做,已列 future work)
|
||||
|
||||
---
|
||||
*完整逐點對照見 `paper/fusion_review_todo.md`(29/29 已處理)。所有分析腳本在 `paper/v13_build/scripts/`,可一鍵重現。*
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Firm x year descriptor trends (B-gate diagnostic).
|
||||
|
||||
Plots per-firm yearly mean cosine, mean dHash, and HC-box hit share to test
|
||||
whether Firms B/C/D show a 2020 structural break converging toward Firm A.
|
||||
Read-only against the production DB.
|
||||
"""
|
||||
import sqlite3
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRMS = [('勤業眾信聯合', 'Firm A (Deloitte)', '#d62728'),
|
||||
('安侯建業聯合', 'Firm B (KPMG)', '#1f77b4'),
|
||||
('資誠聯合', 'Firm C (PwC)', '#2ca02c'),
|
||||
('安永聯合', 'Firm D (EY)', '#ff7f0e')]
|
||||
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
|
||||
|
||||
def series(firm_zh):
|
||||
cur.execute("""
|
||||
SELECT CAST(substr(s.year_month,1,4) AS INT) AS yr,
|
||||
AVG(s.max_similarity_to_same_accountant),
|
||||
AVG(s.min_dhash_independent),
|
||||
AVG(CASE WHEN s.max_similarity_to_same_accountant>0.95
|
||||
AND s.min_dhash_independent<=5 THEN 1.0 ELSE 0.0 END),
|
||||
COUNT(*)
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE a.firm=? AND s.year_month IS NOT NULL
|
||||
AND s.max_similarity_to_same_accountant IS NOT NULL
|
||||
AND s.min_dhash_independent IS NOT NULL
|
||||
GROUP BY yr ORDER BY yr""", (firm_zh,))
|
||||
return cur.fetchall()
|
||||
|
||||
|
||||
fig, axes = plt.subplots(1, 3, figsize=(16, 4.8))
|
||||
for firm_zh, label, color in FIRMS:
|
||||
rows = series(firm_zh)
|
||||
yrs = [r[0] for r in rows]
|
||||
axes[0].plot(yrs, [r[1] for r in rows], 'o-', color=color, label=label)
|
||||
axes[1].plot(yrs, [r[2] for r in rows], 'o-', color=color, label=label)
|
||||
axes[2].plot(yrs, [r[3] for r in rows], 'o-', color=color, label=label)
|
||||
|
||||
for ax in axes:
|
||||
ax.axvline(2020, ls='--', color='grey', alpha=0.6)
|
||||
ax.text(2020.05, ax.get_ylim()[0], ' 2020', color='grey', fontsize=8, va='bottom')
|
||||
ax.set_xlabel('Fiscal year')
|
||||
ax.grid(alpha=0.3)
|
||||
axes[0].set_title('Mean best-match cosine'); axes[0].axhline(0.95, ls=':', color='k', alpha=0.4)
|
||||
axes[1].set_title('Mean independent-min dHash'); axes[1].axhline(5, ls=':', color='k', alpha=0.4)
|
||||
axes[2].set_title('HC-box share (cos>0.95 & dHash$\\leq$5)')
|
||||
axes[0].legend(fontsize=8, loc='lower right')
|
||||
fig.suptitle('Big-4 descriptor trends 2013–2023 (2023 = partial, to Apr) — no 2020 break, no convergence to A',
|
||||
fontsize=11)
|
||||
fig.tight_layout()
|
||||
out = '/Volumes/NV2/pdf_recognize/signature_analysis/firm_year_trends.png'
|
||||
fig.savefig(out, dpi=130, bbox_inches='tight')
|
||||
print('saved', out)
|
||||
conn.close()
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 46: BCD-only (exclude Firm A) per-comparison ICCR recompute.
|
||||
|
||||
Replicates 40b's inter-CPA negative-anchor pair sampling (N=500k, seed=42)
|
||||
but compares three negative-anchor pool compositions:
|
||||
- ABCD : all Big-4 (current paper baseline)
|
||||
- BCD : Big-4 excluding Firm A (normative-baseline proposal)
|
||||
- BCD+nonB4 : BCD plus all non-Big-4 firms
|
||||
Reports marginal cos>0.95, dHash<=5, and the joint HC rule cos>0.95 & dHash<=5.
|
||||
Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
N_PAIRS = 500_000
|
||||
SEED = 42
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
|
||||
|
||||
def hamming(a, b):
|
||||
return (int.from_bytes(a, 'big') ^ int.from_bytes(b, 'big')).bit_count()
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k / n
|
||||
d = 1 + z*z/n
|
||||
c = (p + z*z/(2*n)) / d
|
||||
h = z*np.sqrt(p*(1-p)/n + z*z/(4*n*n)) / d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def iccr(rows, label):
|
||||
by = defaultdict(list)
|
||||
for acct, firm, fv, dh in rows:
|
||||
by[acct].append((fv, dh))
|
||||
accts = list(by.keys())
|
||||
feats = {a: np.stack([np.frombuffer(r[0], dtype=np.float32) for r in by[a]]) for a in accts}
|
||||
dhs = {a: [r[1] for r in by[a]] for a in accts}
|
||||
rng = np.random.default_rng(SEED)
|
||||
cos = np.empty(N_PAIRS, np.float32)
|
||||
dv = np.empty(N_PAIRS, np.int32)
|
||||
na = len(accts)
|
||||
for t in range(N_PAIRS):
|
||||
i, j = rng.choice(na, 2, replace=False)
|
||||
a1, a2 = accts[i], accts[j]
|
||||
k1 = int(rng.integers(0, len(by[a1])))
|
||||
k2 = int(rng.integers(0, len(by[a2])))
|
||||
cos[t] = float(feats[a1][k1] @ feats[a2][k2])
|
||||
dv[t] = hamming(dhs[a1][k1], dhs[a2][k2])
|
||||
n = N_PAIRS
|
||||
m_cos = int((cos > 0.95).sum())
|
||||
m_dh = int((dv <= 5).sum())
|
||||
joint = int(((cos > 0.95) & (dv <= 5)).sum())
|
||||
jlo, jhi = wilson(joint, n)
|
||||
print(f'\n== {label} ==')
|
||||
print(f' signatures={len(rows):,} accountants={na} pairs={n:,}')
|
||||
print(f' cos>0.95 ICCR = {m_cos/n:.5f} ({m_cos})')
|
||||
print(f' dHash<=5 ICCR = {m_dh/n:.5f} ({m_dh})')
|
||||
print(f' JOINT (HC rule) ICCR = {joint/n:.6f} ({joint}) Wilson95% [{jlo:.6f},{jhi:.6f}]')
|
||||
return joint/n
|
||||
|
||||
|
||||
rows = load()
|
||||
abcd = [r for r in rows if r[1] in BIG4]
|
||||
bcd = [r for r in rows if r[1] in BIG4 and r[1] != FIRM_A]
|
||||
bcd_non = [r for r in rows if r[1] != FIRM_A]
|
||||
iccr(abcd, 'ABCD (current paper baseline)')
|
||||
iccr(bcd, 'BCD only (exclude Firm A)')
|
||||
iccr(bcd_non, 'BCD + non-Big-4')
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 47: BCD-only recompute of (1) KDE crossover, (2) per-signature
|
||||
pool-normalized any-pair ICCR (cos>0.95 & dHash<=5), (3) per-document HC+MC
|
||||
inter-CPA ICCR (cos>0.95 & dHash<=15), each for ABCD vs BCD-only negative-anchor
|
||||
pools. Replicates Scripts 10/43/44 methodology. Document-level subsampling used
|
||||
for the pool simulation (exact same-CPA pool sizes retained). Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
from scipy.stats import gaussian_kde
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
N_INTRA = 200_000
|
||||
N_INTER = 500_000
|
||||
N_DOC_SUBSAMPLE = 9000 # documents processed in pool simulation per scope
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.signature_id, s.assigned_accountant, a.firm, s.source_pdf,
|
||||
s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IN (?,?,?,?)
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def hamming1(q, c):
|
||||
return (int.from_bytes(q, 'big') ^ int.from_bytes(c, 'big')).bit_count()
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def kde_crossover(feats, cpas, label):
|
||||
by = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
by[c].append(i)
|
||||
by = {c: np.array(v) for c, v in by.items() if len(v) >= 2}
|
||||
accts = list(by.keys())
|
||||
rng = np.random.default_rng(SEED)
|
||||
# intra: two sigs from same random CPA
|
||||
intra = np.empty(N_INTRA, np.float32)
|
||||
ks = rng.integers(0, len(accts), N_INTRA)
|
||||
for t in range(N_INTRA):
|
||||
idx = by[accts[ks[t]]]
|
||||
a, b = rng.choice(idx, 2, replace=False)
|
||||
intra[t] = feats[a] @ feats[b]
|
||||
# inter: two sigs from different CPAs
|
||||
inter = np.empty(N_INTER, np.float32)
|
||||
for t in range(N_INTER):
|
||||
i, j = rng.choice(len(accts), 2, replace=False)
|
||||
a = rng.choice(by[accts[i]]); b = rng.choice(by[accts[j]])
|
||||
inter[t] = feats[a] @ feats[b]
|
||||
xs = np.linspace(0.3, 1.0, 10000)
|
||||
ki = gaussian_kde(intra[:100000]); ke = gaussian_kde(inter[:100000])
|
||||
diff = ki(xs) - ke(xs)
|
||||
cross = xs[np.where(np.diff(np.sign(diff)))[0]]
|
||||
cross = [float(x) for x in cross if 0.6 < x < 0.99]
|
||||
print(f' [{label}] intra mean={intra.mean():.4f} inter mean={inter.mean():.4f}'
|
||||
f' KDE crossover(s): {[f"{x:.4f}" for x in cross]}')
|
||||
return cross
|
||||
|
||||
|
||||
def pool_sim(rows, scope_firms, label):
|
||||
"""Per-signature & per-document inter-CPA any-pair ICCR over a doc subsample."""
|
||||
keep = [r for r in rows if ALIAS[r[2]] in scope_firms]
|
||||
feats = np.stack([np.frombuffer(r[4], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
cpas = [r[1] for r in keep]
|
||||
firms = [ALIAS[r[2]] for r in keep]
|
||||
docs = [r[3] for r in keep]
|
||||
dh = [r[5] for r in keep]
|
||||
n = len(keep)
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
doc_idx = defaultdict(list)
|
||||
for i, d in enumerate(docs):
|
||||
doc_idx[d].append(i)
|
||||
rng = np.random.default_rng(SEED)
|
||||
all_docs = list(doc_idx.keys())
|
||||
sub = rng.choice(len(all_docs), min(N_DOC_SUBSAMPLE, len(all_docs)), replace=False)
|
||||
sel_docs = [all_docs[i] for i in sub]
|
||||
|
||||
sig_hc = [] # per-signature: any-pair cos>0.95 & dh<=5
|
||||
sig_firm = []
|
||||
doc_hcmc = {} # per-document worst-case: any sig with cos>0.95 & dh<=15
|
||||
doc_firm = {}
|
||||
for d in sel_docs:
|
||||
dhit = False
|
||||
for si in doc_idx[d]:
|
||||
c = cpas[si]; npool = pool_size[c]
|
||||
if npool <= 0:
|
||||
sig_hc.append(False); sig_firm.append(firms[si]); continue
|
||||
same = cpa_idx[c]
|
||||
draw = rng.choice(n, size=min(npool*2+10, n), replace=True)
|
||||
cand = draw[~np.isin(draw, same)][:npool]
|
||||
cosv = feats[cand] @ feats[si]
|
||||
dhv = np.fromiter((hamming1(dh[si], dh[c2]) for c2 in cand), np.int32, len(cand))
|
||||
cg = cosv > 0.95
|
||||
hc = bool((cg & (dhv <= 5)).any())
|
||||
hcmc = bool((cg & (dhv <= 15)).any())
|
||||
sig_hc.append(hc); sig_firm.append(firms[si])
|
||||
if hcmc:
|
||||
dhit = True
|
||||
doc_hcmc[d] = dhit
|
||||
doc_firm[d] = firms[doc_idx[d][0]]
|
||||
|
||||
sig_hc = np.array(sig_hc); sig_firm = np.array(sig_firm)
|
||||
k = int(sig_hc.sum()); m = len(sig_hc)
|
||||
lo, hi = wilson(k, m)
|
||||
print(f'\n [{label}] per-SIGNATURE any-pair HC ICCR (cos>0.95 & dh<=5): '
|
||||
f'{k/m:.4f} ({k}/{m}) Wilson95% [{lo:.4f},{hi:.4f}]')
|
||||
for f in sorted(set(sig_firm)):
|
||||
msk = sig_firm == f
|
||||
kk = int(sig_hc[msk].sum()); mm = int(msk.sum())
|
||||
print(f' Firm {f}: {kk/mm:.4f} ({kk}/{mm})')
|
||||
dvals = np.array(list(doc_hcmc.values())); dfirm = np.array(list(doc_firm.values()))
|
||||
dk = int(dvals.sum()); dm = len(dvals)
|
||||
dlo, dhi = wilson(dk, dm)
|
||||
print(f' [{label}] per-DOCUMENT HC+MC ICCR (cos>0.95 & dh<=15): '
|
||||
f'{dk/dm:.4f} ({dk}/{dm}) Wilson95% [{dlo:.4f},{dhi:.4f}]')
|
||||
for f in sorted(set(dfirm)):
|
||||
msk = dfirm == f
|
||||
kk = int(dvals[msk].sum()); mm = int(msk.sum())
|
||||
print(f' Firm {f}: {kk/mm:.4f} ({kk}/{mm})')
|
||||
|
||||
|
||||
rows = load()
|
||||
allf = np.stack([np.frombuffer(r[4], np.float32) for r in rows]).astype(np.float32)
|
||||
allf /= np.clip(np.linalg.norm(allf, axis=1, keepdims=True), 1e-9, None)
|
||||
allc = [r[1] for r in rows]
|
||||
abcd_mask = [True]*len(rows)
|
||||
bcd_mask = [r[2] != FIRM_A for r in rows]
|
||||
|
||||
print('=== (1) KDE crossover (intra vs inter cosine) ===')
|
||||
kde_crossover(allf, allc, 'ABCD')
|
||||
kde_crossover(allf[bcd_mask], [allc[i] for i in range(len(rows)) if bcd_mask[i]], 'BCD-only')
|
||||
|
||||
print('\n=== (2)(3) per-signature & per-document inter-CPA ICCR ===')
|
||||
pool_sim(rows, {'A', 'B', 'C', 'D'}, 'ABCD (reproduce)')
|
||||
pool_sim(rows, {'B', 'C', 'D'}, 'BCD-only')
|
||||
@@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 48: full-fidelity (no subsample) BCD-only recompute of per-signature
|
||||
and per-document inter-CPA any-pair ICCR, plus corpus-style KDE crossover.
|
||||
Vectorized popcount. Scopes: ABCD, BCD-only, BCD+non-Big-4. Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
from scipy.stats import gaussian_kde
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.source_pdf,
|
||||
s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def prep(rows, keep_fn):
|
||||
keep = [r for r in rows if keep_fn(r[1])]
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
dh = np.stack([np.frombuffer(r[4], np.uint8) for r in keep]) # (n,8)
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
firms = np.array([ALIAS.get(r[1], 'X') for r in keep])
|
||||
docs = np.array([r[2] for r in keep])
|
||||
return feats, dh, cpas, firms, docs
|
||||
|
||||
|
||||
def crossover(feats, cpas, label):
|
||||
by = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
by[c].append(i)
|
||||
by = {c: np.array(v) for c, v in by.items() if len(v) >= 2}
|
||||
accts = list(by.keys())
|
||||
rng = np.random.default_rng(SEED)
|
||||
N = 100_000
|
||||
intra = np.empty(N, np.float32); inter = np.empty(N, np.float32)
|
||||
ks = rng.integers(0, len(accts), N)
|
||||
for t in range(N):
|
||||
idx = by[accts[ks[t]]]
|
||||
a, b = rng.choice(idx, 2, replace=False)
|
||||
intra[t] = feats[a] @ feats[b]
|
||||
i, j = rng.choice(len(accts), 2, replace=False)
|
||||
inter[t] = feats[rng.choice(by[accts[i]])] @ feats[rng.choice(by[accts[j]])]
|
||||
xs = np.linspace(0.3, 1.0, 10000)
|
||||
diff = gaussian_kde(intra)(xs) - gaussian_kde(inter)(xs)
|
||||
cross = [float(x) for x in xs[np.where(np.diff(np.sign(diff)))[0]] if 0.6 < x < 0.99]
|
||||
print(f' [{label}] crossover {[f"{x:.4f}" for x in cross]} '
|
||||
f'(intra {intra.mean():.4f} / inter {inter.mean():.4f})')
|
||||
|
||||
|
||||
def pool_sim(feats, dh, cpas, firms, docs, label):
|
||||
n = len(cpas)
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
sig_hc = np.zeros(n, bool)
|
||||
doc_hcmc = defaultdict(bool)
|
||||
for si in range(n):
|
||||
c = cpas[si]; npool = pool_size[c]
|
||||
if npool <= 0:
|
||||
continue
|
||||
same = cpa_idx[c]
|
||||
draw = rng.integers(0, n, size=npool + same.size + 20)
|
||||
cand = draw[~np.isin(draw, same)][:npool]
|
||||
cosv = feats[cand] @ feats[si]
|
||||
cg = cosv > 0.95
|
||||
if cg.any():
|
||||
dist = POP[dh[cand] ^ dh[si]].sum(axis=1)
|
||||
sig_hc[si] = bool((cg & (dist <= 5)).any())
|
||||
if (cg & (dist <= 15)).any():
|
||||
doc_hcmc[docs[si]] = True
|
||||
else:
|
||||
doc_hcmc.setdefault(docs[si], doc_hcmc[docs[si]] if docs[si] in doc_hcmc else False)
|
||||
# ensure every doc present
|
||||
for d in docs:
|
||||
doc_hcmc.setdefault(d, False)
|
||||
k = int(sig_hc.sum())
|
||||
lo, hi = wilson(k, n)
|
||||
print(f'\n [{label}] per-SIGNATURE any-pair HC (cos>0.95 & dh<=5): '
|
||||
f'{k/n:.4f} ({k}/{n}) Wilson95% [{lo:.4f},{hi:.4f}]')
|
||||
for f in sorted(set(firms)):
|
||||
m = firms == f
|
||||
print(f' Firm {f}: {sig_hc[m].sum()/m.sum():.4f} ({int(sig_hc[m].sum())}/{int(m.sum())})')
|
||||
# per-doc, with firm of first sig
|
||||
dfirm = {}
|
||||
for i, d in enumerate(docs):
|
||||
dfirm.setdefault(d, firms[i])
|
||||
dl = list(doc_hcmc.keys())
|
||||
dv = np.array([doc_hcmc[d] for d in dl])
|
||||
df = np.array([dfirm[d] for d in dl])
|
||||
dk = int(dv.sum()); dm = len(dv)
|
||||
dlo, dhi = wilson(dk, dm)
|
||||
print(f' [{label}] per-DOCUMENT HC+MC (cos>0.95 & dh<=15): '
|
||||
f'{dk/dm:.4f} ({dk}/{dm}) Wilson95% [{dlo:.4f},{dhi:.4f}]')
|
||||
for f in sorted(set(df)):
|
||||
m = df == f
|
||||
print(f' Firm {f}: {dv[m].sum()/m.sum():.4f} ({int(dv[m].sum())}/{int(m.sum())})')
|
||||
|
||||
|
||||
rows = load()
|
||||
SCOPES = [('ABCD', lambda fm: fm in BIG4),
|
||||
('BCD-only', lambda fm: fm in BIG4 and fm != FIRM_A),
|
||||
('BCD+nonBig4', lambda fm: fm != FIRM_A)]
|
||||
|
||||
print('=== KDE crossover ===')
|
||||
for name, fn in SCOPES[:2]:
|
||||
f, _, c, _, _ = prep(rows, fn)
|
||||
crossover(f, c, name)
|
||||
|
||||
print('\n=== per-signature & per-document inter-CPA ICCR (full) ===')
|
||||
for name, fn in SCOPES:
|
||||
f, dh, c, fm, dc = prep(rows, fn)
|
||||
pool_sim(f, dh, c, fm, dc, name)
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 49: Firm A as out-of-sample target against a clean BCD baseline.
|
||||
(1) A signatures scored against a BCD-only candidate pool (true out-of-sample
|
||||
inter-firm coincidence).
|
||||
(2) Observed deployed rate on ACTUAL same-CPA pools, per firm (the real fired
|
||||
rate, from precomputed deployed descriptors), to juxtapose against the
|
||||
clean BCD inter-CPA coincidence floor. Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.source_pdf, s.feature_vector,
|
||||
s.dhash_vector, s.max_similarity_to_same_accountant, s.min_dhash_independent
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IN (?,?,?,?)
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
# ---- (1) Firm A source vs BCD-only candidate pool ----
|
||||
print('=== (1) Firm A out-of-sample vs clean BCD candidate pool ===')
|
||||
A = [r for r in rows if r[1] == FIRM_A]
|
||||
BCD = [r for r in rows if r[1] in BIG4 and r[1] != FIRM_A]
|
||||
bcd_feat = np.stack([np.frombuffer(r[3], np.float32) for r in BCD]).astype(np.float32)
|
||||
bcd_feat /= np.clip(np.linalg.norm(bcd_feat, axis=1, keepdims=True), 1e-9, None)
|
||||
bcd_dh = np.stack([np.frombuffer(r[4], np.uint8) for r in BCD])
|
||||
nb = len(BCD)
|
||||
# A CPA pool sizes (their own same-CPA count - 1), to match negative-anchor construction
|
||||
a_cpa_idx = defaultdict(list)
|
||||
for i, r in enumerate(A):
|
||||
a_cpa_idx[r[0]].append(i)
|
||||
pool_size = {c: len(v)-1 for c, v in a_cpa_idx.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
sig_hc = np.zeros(len(A), bool)
|
||||
doc_hcmc = defaultdict(bool)
|
||||
for i, r in enumerate(A):
|
||||
npool = max(pool_size[r[0]], 1)
|
||||
cand = rng.integers(0, nb, size=npool)
|
||||
sf = np.frombuffer(r[3], np.float32).astype(np.float32)
|
||||
sf /= max(np.linalg.norm(sf), 1e-9)
|
||||
cosv = bcd_feat[cand] @ sf
|
||||
cg = cosv > 0.95
|
||||
doc_hcmc.setdefault(r[2], False)
|
||||
if cg.any():
|
||||
dist = POP[bcd_dh[cand] ^ np.frombuffer(r[4], np.uint8)].sum(axis=1)
|
||||
sig_hc[i] = bool((cg & (dist <= 5)).any())
|
||||
if (cg & (dist <= 15)).any():
|
||||
doc_hcmc[r[2]] = True
|
||||
k = int(sig_hc.sum()); n = len(A); lo, hi = wilson(k, n)
|
||||
print(f' A-source vs BCD-pool per-SIGNATURE HC (cos>0.95 & dh<=5): '
|
||||
f'{k/n:.4f} ({k}/{n}) Wilson95% [{lo:.4f},{hi:.4f}]')
|
||||
dv = np.array(list(doc_hcmc.values())); dk = int(dv.sum()); dm = len(dv)
|
||||
dlo, dhi = wilson(dk, dm)
|
||||
print(f' A-source vs BCD-pool per-DOCUMENT HC+MC (cos>0.95 & dh<=15): '
|
||||
f'{dk/dm:.4f} ({dk}/{dm}) Wilson95% [{dlo:.4f},{dhi:.4f}]')
|
||||
|
||||
# ---- (2) Observed deployed rate on ACTUAL same-CPA pools, per firm ----
|
||||
print('\n=== (2) Observed deployed rate on actual same-CPA pools (real fired rate) ===')
|
||||
print(' per-signature HC = max_sim>0.95 & min_dh<=5 ; per-doc HC+MC worst-case dh<=15')
|
||||
by_firm_sig = defaultdict(lambda: [0, 0])
|
||||
doc_obs = {}
|
||||
doc_firm = {}
|
||||
for r in rows:
|
||||
fm = ALIAS[r[1]]
|
||||
ms, md = r[5], r[6]
|
||||
if ms is None or md is None:
|
||||
continue
|
||||
hc = (ms > 0.95) and (md <= 5)
|
||||
hcmc = (ms > 0.95) and (md <= 15)
|
||||
by_firm_sig[fm][0] += int(hc); by_firm_sig[fm][1] += 1
|
||||
doc_firm.setdefault(r[2], fm)
|
||||
doc_obs[r[2]] = doc_obs.get(r[2], False) or hcmc
|
||||
for fm in sorted(by_firm_sig):
|
||||
k, n = by_firm_sig[fm]
|
||||
lo, hi = wilson(k, n)
|
||||
print(f' Firm {fm} per-SIGNATURE HC: {k/n:.4f} ({k}/{n}) [{lo:.4f},{hi:.4f}]')
|
||||
dd = defaultdict(lambda: [0, 0])
|
||||
for d, hit in doc_obs.items():
|
||||
fm = doc_firm[d]; dd[fm][0] += int(hit); dd[fm][1] += 1
|
||||
for fm in sorted(dd):
|
||||
k, n = dd[fm]
|
||||
print(f' Firm {fm} per-DOCUMENT HC+MC: {k/n:.4f} ({k}/{n})')
|
||||
print(f'\n Clean BCD inter-CPA coincidence FLOOR: per-sig HC=0.0048, per-doc HC+MC=0.1281')
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 50: publication-grade scoped inter-CPA anchor recompute.
|
||||
Faithfully reproduces Script 45's any-pair five-way pool simulation
|
||||
(max_cos & min_dh over a random same-size inter-CPA pool, excl. same-CPA),
|
||||
then reports for scopes ABCD / BCD / BCD+nonBig4:
|
||||
- per-signature HC (D1) and HC+MC (D2) any-pair FAR
|
||||
- per-document HC (D1) and HC+MC (D2) any-pair FAR
|
||||
- per-firm per-document D2
|
||||
ABCD is printed first to verify reproduction of published values
|
||||
(per-sig HC~0.1102, per-doc D2~0.3375, Firm A~0.62). Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.source_pdf,
|
||||
s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def run(rows, keep_fn, label):
|
||||
keep = [r for r in rows if keep_fn(r[1])]
|
||||
n = len(keep)
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
dh = np.stack([np.frombuffer(r[4], np.uint8) for r in keep])
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
firms = np.array([ALIAS.get(r[1], 'NonB4') for r in keep])
|
||||
docs = np.array([r[2] for r in keep])
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
max_cos = np.zeros(n, np.float32)
|
||||
min_dh = np.full(n, 64, np.int32)
|
||||
for si in range(n):
|
||||
c = cpas[si]; npool = pool_size[c]
|
||||
if npool <= 0:
|
||||
continue
|
||||
same = cpa_idx[c]
|
||||
draw = rng.integers(0, n, size=npool + same.size + 20)
|
||||
cand = draw[~np.isin(draw, same)][:npool]
|
||||
cosv = feats[cand] @ feats[si]
|
||||
dist = POP[dh[cand] ^ dh[si]].sum(axis=1)
|
||||
max_cos[si] = cosv.max()
|
||||
min_dh[si] = int(dist.min())
|
||||
# any-pair classification
|
||||
hc = (max_cos > 0.95) & (min_dh <= 5)
|
||||
mc = (max_cos > 0.95) & (min_dh > 5) & (min_dh <= 15)
|
||||
d1 = hc
|
||||
d2 = hc | mc
|
||||
print(f'\n===== {label} (n_sig={n:,}) =====')
|
||||
for nm, arr in [('per-sig HC (D1)', d1), ('per-sig HC+MC (D2)', d2)]:
|
||||
k = int(arr.sum()); lo, hi = wilson(k, n)
|
||||
print(f' {nm}: {k/n:.4f} ({k}/{n}) [{lo:.4f},{hi:.4f}]')
|
||||
# per-document worst-case
|
||||
doc_d1 = defaultdict(bool); doc_d2 = defaultdict(bool); doc_firm = {}
|
||||
for i in range(n):
|
||||
if d1[i]: doc_d1[docs[i]] = True
|
||||
if d2[i]: doc_d2[docs[i]] = True
|
||||
doc_firm.setdefault(docs[i], firms[i])
|
||||
doc_d1.setdefault(docs[i], False); doc_d2.setdefault(docs[i], False)
|
||||
dl = list(doc_d2.keys())
|
||||
nd = len(dl)
|
||||
k1 = sum(doc_d1[d] for d in dl); k2 = sum(doc_d2[d] for d in dl)
|
||||
l1 = wilson(k1, nd); l2 = wilson(k2, nd)
|
||||
print(f' per-doc HC (D1): {k1/nd:.4f} ({k1}/{nd}) [{l1[0]:.4f},{l1[1]:.4f}]')
|
||||
print(f' per-doc HC+MC (D2):{k2/nd:.4f} ({k2}/{nd}) [{l2[0]:.4f},{l2[1]:.4f}]')
|
||||
df = np.array([doc_firm[d] for d in dl])
|
||||
dv = np.array([doc_d2[d] for d in dl])
|
||||
for f in sorted(set(df)):
|
||||
m = df == f
|
||||
print(f' Firm {f} per-doc D2: {dv[m].sum()/m.sum():.4f} ({int(dv[m].sum())}/{int(m.sum())})')
|
||||
|
||||
|
||||
rows = load()
|
||||
run(rows, lambda fm: fm in BIG4, 'ABCD (verify vs published: HC~0.110 / D2~0.338 / A~0.62)')
|
||||
run(rows, lambda fm: fm in BIG4 and fm != FIRM_A, 'BCD-only')
|
||||
run(rows, lambda fm: fm != FIRM_A, 'BCD + non-Big4')
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 51: publication polish.
|
||||
Part A: CPA-block bootstrap (1000 reps) on per-signature HC any-pair rate, and
|
||||
document-level bootstrap on per-document HC+MC, for ABCD & BCD.
|
||||
Part B: corpus-wide KDE crossover (pair-weighted intra, reproduce 0.837) plus
|
||||
BCD-only and BCD+nonBig4 variants.
|
||||
Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
from scipy.stats import gaussian_kde
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
N_BOOT = 1000
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.source_pdf,
|
||||
s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
# ============ Part A: bootstrap on anchor rates ============
|
||||
def simulate(keep):
|
||||
n = len(keep)
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
dh = np.stack([np.frombuffer(r[4], np.uint8) for r in keep])
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
docs = np.array([r[2] for r in keep])
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
max_cos = np.zeros(n, np.float32); min_dh = np.full(n, 64, np.int32)
|
||||
for si in range(n):
|
||||
c = cpas[si]; npool = pool_size[c]
|
||||
if npool <= 0:
|
||||
continue
|
||||
same = cpa_idx[c]
|
||||
draw = rng.integers(0, n, size=npool + same.size + 20)
|
||||
cand = draw[~np.isin(draw, same)][:npool]
|
||||
cosv = feats[cand] @ feats[si]
|
||||
dist = POP[dh[cand] ^ dh[si]].sum(axis=1)
|
||||
max_cos[si] = cosv.max(); min_dh[si] = int(dist.min())
|
||||
hc = (max_cos > 0.95) & (min_dh <= 5)
|
||||
d2 = (max_cos > 0.95) & (min_dh <= 15)
|
||||
return hc, d2, cpa_idx, docs
|
||||
|
||||
|
||||
def boot_part(keep, label):
|
||||
hc, d2, cpa_idx, docs = simulate(keep)
|
||||
n = len(hc)
|
||||
rng = np.random.default_rng(SEED + 1)
|
||||
cpa_list = list(cpa_idx.keys())
|
||||
# CPA-block bootstrap on per-signature HC
|
||||
bs = np.empty(N_BOOT)
|
||||
for b in range(N_BOOT):
|
||||
cs = rng.choice(len(cpa_list), len(cpa_list), replace=True)
|
||||
idx = np.concatenate([cpa_idx[cpa_list[i]] for i in cs])
|
||||
bs[b] = hc[idx].mean()
|
||||
# document-level bootstrap on per-doc D2
|
||||
doc_d2 = defaultdict(bool)
|
||||
for i in range(n):
|
||||
doc_d2[docs[i]] = doc_d2[docs[i]] or bool(d2[i])
|
||||
dl = np.array(list(doc_d2.keys())); dvals = np.array([doc_d2[d] for d in dl])
|
||||
nd = len(dl); bd = np.empty(N_BOOT)
|
||||
for b in range(N_BOOT):
|
||||
s = rng.integers(0, nd, nd)
|
||||
bd[b] = dvals[s].mean()
|
||||
print(f'\n [{label}] per-sig HC point={hc.mean():.4f} '
|
||||
f'CPA-block boot95% [{np.percentile(bs,2.5):.4f}, {np.percentile(bs,97.5):.4f}]')
|
||||
print(f' [{label}] per-doc HC+MC point={dvals.mean():.4f} '
|
||||
f'doc boot95% [{np.percentile(bd,2.5):.4f}, {np.percentile(bd,97.5):.4f}]')
|
||||
|
||||
|
||||
# ============ Part B: pair-weighted KDE crossover ============
|
||||
def crossover(keep, label):
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
by = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
by[c].append(i)
|
||||
by = {c: np.array(v) for c, v in by.items() if len(v) >= 3}
|
||||
accts = list(by.keys())
|
||||
pair_w = np.array([len(by[c])*(len(by[c])-1)/2 for c in accts], float)
|
||||
pair_w /= pair_w.sum()
|
||||
rng = np.random.default_rng(SEED)
|
||||
M = 100_000
|
||||
# intra: CPA sampled proportional to pair count (= uniform over all intra pairs)
|
||||
intra = np.empty(M, np.float32)
|
||||
ci = rng.choice(len(accts), M, p=pair_w)
|
||||
for t in range(M):
|
||||
a, b = rng.choice(by[accts[ci[t]]], 2, replace=False)
|
||||
intra[t] = feats[a] @ feats[b]
|
||||
inter = np.empty(M, np.float32)
|
||||
for t in range(M):
|
||||
i, j = rng.choice(len(accts), 2, replace=False)
|
||||
inter[t] = feats[rng.choice(by[accts[i]])] @ feats[rng.choice(by[accts[j]])]
|
||||
xs = np.linspace(0.3, 1.0, 10000)
|
||||
diff = gaussian_kde(intra)(xs) - gaussian_kde(inter)(xs)
|
||||
cr = [float(x) for x in xs[np.where(np.diff(np.sign(diff)))[0]] if 0.6 < x < 0.99]
|
||||
print(f' [{label}] crossover {[f"{x:.4f}" for x in cr]} '
|
||||
f'(intra {intra.mean():.4f}/{np.median(intra):.4f} inter {inter.mean():.4f}/{np.median(inter):.4f})')
|
||||
|
||||
|
||||
rows = load()
|
||||
abcd = [r for r in rows if r[1] in BIG4]
|
||||
bcd = [r for r in rows if r[1] in BIG4 and r[1] != FIRM_A]
|
||||
|
||||
print('=== Part A: bootstrap CIs on anchor rates ===')
|
||||
boot_part(abcd, 'ABCD (verify ~0.109 / ~0.338)')
|
||||
boot_part(bcd, 'BCD-only')
|
||||
|
||||
print('\n=== Part B: KDE crossover (pair-weighted intra, corpus-wide reproduces 0.837) ===')
|
||||
crossover(rows, 'corpus-wide (all firms)')
|
||||
crossover(bcd, 'BCD-only')
|
||||
crossover([r for r in rows if r[1] != FIRM_A], 'BCD + non-Big4')
|
||||
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 52: canonical-correct locked publication numbers (supersedes 48/50,
|
||||
fixes the per-firm assignment and A-out-of-sample any-pair issues codex flagged).
|
||||
|
||||
Uses the EXACT canonical candidate sampler of Scripts 43/45 (retry-loop to
|
||||
collect exactly n_pool non-same-CPA candidates, rng.choice, default_rng(42)),
|
||||
any-pair max-cos/min-dHash five-way classification, and dominant-firm document
|
||||
assignment. Scopes: ABCD / BCD / BCD+nonBig4, plus Firm-A out-of-sample vs a
|
||||
clean BCD candidate pool. Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict, Counter
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
N_BOOT = 1000
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def load():
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, s.source_pdf,
|
||||
s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""")
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
return rows
|
||||
|
||||
|
||||
def canonical_sampler(rng, n, n_pool, same_cpa, all_idx):
|
||||
"""EXACT Scripts 43/45 sampler: retry-loop to exactly n_pool non-same."""
|
||||
need = n_pool
|
||||
cand = []
|
||||
attempts = 0
|
||||
while need > 0 and attempts < 10:
|
||||
draw = rng.choice(n, size=need * 2, replace=True)
|
||||
ok = draw[~np.isin(draw, same_cpa)]
|
||||
cand.extend(ok[:need].tolist())
|
||||
need -= len(ok[:need])
|
||||
attempts += 1
|
||||
if need > 0:
|
||||
pool_mask = np.ones(n, dtype=bool)
|
||||
pool_mask[same_cpa] = False
|
||||
fb = rng.choice(all_idx[pool_mask], size=need, replace=False)
|
||||
cand.extend(fb.tolist())
|
||||
return np.array(cand[:n_pool], dtype=np.int64)
|
||||
|
||||
|
||||
def simulate(keep):
|
||||
n = len(keep)
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
norms = np.linalg.norm(feats, axis=1, keepdims=True); norms[norms == 0] = 1.0
|
||||
feats = feats / norms
|
||||
dh = np.stack([np.frombuffer(r[4], np.uint8) for r in keep])
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
firms = np.array([ALIAS.get(r[1], 'NonB4') for r in keep])
|
||||
docs = np.array([r[2] for r in keep])
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
all_idx = np.arange(n)
|
||||
rng = np.random.default_rng(SEED)
|
||||
max_cos = np.zeros(n, np.float32); min_dh = np.full(n, 64, np.int32)
|
||||
for si in range(n):
|
||||
np_ = pool_size[cpas[si]]
|
||||
if np_ <= 0:
|
||||
continue
|
||||
cand = canonical_sampler(rng, n, np_, cpa_idx[cpas[si]], all_idx)
|
||||
cosv = feats[cand] @ feats[si]
|
||||
dist = POP[dh[cand] ^ dh[si]].sum(axis=1)
|
||||
max_cos[si] = cosv.max(); min_dh[si] = int(dist.min())
|
||||
return max_cos, min_dh, cpas, firms, docs, cpa_idx
|
||||
|
||||
|
||||
def report(keep, label):
|
||||
max_cos, min_dh, cpas, firms, docs, cpa_idx = simulate(keep)
|
||||
n = len(cpas)
|
||||
hc = (max_cos > 0.95) & (min_dh <= 5)
|
||||
d2 = (max_cos > 0.95) & (min_dh <= 15)
|
||||
print(f'\n===== {label} (n_sig={n:,}) =====')
|
||||
for nm, a in [('per-sig HC', hc), ('per-sig HC+MC', d2)]:
|
||||
k = int(a.sum()); lo, hi = wilson(k, n)
|
||||
print(f' {nm}: {k/n:.6f} ({k}/{n}) [{lo:.4f},{hi:.4f}]')
|
||||
# CPA-block bootstrap on per-sig HC
|
||||
rng = np.random.default_rng(SEED + 1)
|
||||
cl = list(cpa_idx.keys())
|
||||
bs = np.empty(N_BOOT)
|
||||
for b in range(N_BOOT):
|
||||
cs = rng.choice(len(cl), len(cl), replace=True)
|
||||
idx = np.concatenate([cpa_idx[cl[i]] for i in cs])
|
||||
bs[b] = hc[idx].mean()
|
||||
print(f' per-sig HC CPA-block boot95% [{np.percentile(bs,2.5):.4f},{np.percentile(bs,97.5):.4f}]')
|
||||
# per-doc, dominant-firm assignment (canonical)
|
||||
doc_sigs = defaultdict(list)
|
||||
for i in range(n):
|
||||
doc_sigs[docs[i]].append(i)
|
||||
dl = list(doc_sigs.keys()); nd = len(dl)
|
||||
doc_d1 = np.array([hc[doc_sigs[d]].any() for d in dl])
|
||||
doc_d2 = np.array([d2[doc_sigs[d]].any() for d in dl])
|
||||
doc_firm = np.array([Counter(firms[doc_sigs[d]]).most_common(1)[0][0] for d in dl])
|
||||
print(f' per-doc HC: {doc_d1.mean():.6f} ({int(doc_d1.sum())}/{nd})')
|
||||
print(f' per-doc HC+MC: {doc_d2.mean():.6f} ({int(doc_d2.sum())}/{nd})')
|
||||
for f in sorted(set(doc_firm)):
|
||||
m = doc_firm == f
|
||||
print(f' Firm {f} per-doc D2: {doc_d2[m].mean():.4f} ({int(doc_d2[m].sum())}/{int(m.sum())})')
|
||||
|
||||
|
||||
def a_out_of_sample(rows):
|
||||
"""Firm A source vs clean BCD candidate pool, any-pair, pool=count-1."""
|
||||
A = [r for r in rows if r[1] == FIRM_A]
|
||||
BCD = [r for r in rows if r[1] in BIG4 and r[1] != FIRM_A]
|
||||
bf = np.stack([np.frombuffer(r[3], np.float32) for r in BCD]).astype(np.float32)
|
||||
nb = bf.shape[0]
|
||||
bn = np.linalg.norm(bf, axis=1, keepdims=True); bn[bn == 0] = 1.0; bf = bf/bn
|
||||
bdh = np.stack([np.frombuffer(r[4], np.uint8) for r in BCD])
|
||||
a_cpa = defaultdict(list)
|
||||
for i, r in enumerate(A):
|
||||
a_cpa[r[0]].append(i)
|
||||
pool_size = {c: len(v)-1 for c, v in a_cpa.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
hc = np.zeros(len(A), bool); d2 = np.zeros(len(A), bool)
|
||||
docs = np.array([r[2] for r in A])
|
||||
for i, r in enumerate(A):
|
||||
np_ = pool_size[r[0]]
|
||||
if np_ <= 0: # singleton CPA: no same-CPA pool, skip (canonical)
|
||||
continue
|
||||
cand = rng.choice(nb, size=np_, replace=True) # A not in BCD pool
|
||||
sf = np.frombuffer(r[3], np.float32).astype(np.float32)
|
||||
sf = sf/max(np.linalg.norm(sf), 1e-9)
|
||||
cosv = bf[cand] @ sf
|
||||
dist = POP[bdh[cand] ^ np.frombuffer(r[4], np.uint8)].sum(axis=1)
|
||||
mc, md = cosv.max(), int(dist.min())
|
||||
hc[i] = (mc > 0.95) and (md <= 5)
|
||||
d2[i] = (mc > 0.95) and (md <= 15)
|
||||
k = int(hc.sum()); n = len(A); lo, hi = wilson(k, n)
|
||||
print(f'\n===== Firm A out-of-sample vs clean BCD pool (any-pair) =====')
|
||||
print(f' per-sig HC: {k/n:.6f} ({k}/{n}) [{lo:.5f},{hi:.5f}]')
|
||||
ds = defaultdict(list)
|
||||
for i in range(n):
|
||||
ds[docs[i]].append(i)
|
||||
dl = list(ds.keys())
|
||||
dd2 = np.array([d2[ds[d]].any() for d in dl])
|
||||
print(f' per-doc HC+MC: {dd2.mean():.6f} ({int(dd2.sum())}/{len(dl)})')
|
||||
|
||||
|
||||
rows = load()
|
||||
report([r for r in rows if r[1] in BIG4], 'ABCD (verify: per-sig HC~0.1102 / per-doc D2~0.3375)')
|
||||
report([r for r in rows if r[1] in BIG4 and r[1] != FIRM_A], 'BCD-only (verify codex: HC~0.0116 / doc HC~0.0226 / doc D2~0.1905)')
|
||||
report([r for r in rows if r[1] != FIRM_A], 'BCD + non-Big4')
|
||||
a_out_of_sample(rows)
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 53: BCD-only firm-effect logistic regression (Firm D reference) and
|
||||
BCD-only cross-firm hit matrix. Candidate pool = BCD (exclude Firm A and
|
||||
same-CPA). Canonical retry-loop sampler, any-pair + same-pair. Read-only.
|
||||
Replicates Script 44's logistic_fit and matrix logic, restricted to BCD.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def logistic_fit(X, y, max_iter=200, l2=0.001):
|
||||
n, k = X.shape
|
||||
beta = np.zeros(k)
|
||||
for _ in range(max_iter):
|
||||
eta = np.clip(X @ beta, -30, 30)
|
||||
p = 1.0/(1.0+np.exp(-eta))
|
||||
grad = X.T @ (y-p) - l2*beta
|
||||
W = p*(1-p)
|
||||
H = -(X.T*W) @ X - l2*np.eye(k)
|
||||
try:
|
||||
delta = np.linalg.solve(H, grad)
|
||||
except np.linalg.LinAlgError:
|
||||
delta = 0.3*grad
|
||||
nb = beta - delta
|
||||
if np.max(np.abs(nb-beta)) < 1e-8:
|
||||
beta = nb; break
|
||||
beta = nb
|
||||
eta = np.clip(X @ beta, -30, 30)
|
||||
p = 1.0/(1.0+np.exp(-eta)); W = p*(1-p)
|
||||
cov = np.linalg.inv((X.T*W) @ X + l2*np.eye(k))
|
||||
return beta, np.sqrt(np.diag(cov))
|
||||
|
||||
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT s.assigned_accountant, a.firm, s.feature_vector, s.dhash_vector
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE s.assigned_accountant IS NOT NULL AND a.firm IN (?,?,?)
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""",
|
||||
('安侯建業聯合', '資誠聯合', '安永聯合'))
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
n = len(rows)
|
||||
feats = np.stack([np.frombuffer(r[2], np.float32) for r in rows]).astype(np.float32)
|
||||
norms = np.linalg.norm(feats, axis=1, keepdims=True); norms[norms == 0] = 1.0
|
||||
feats = feats / norms
|
||||
dh = np.stack([np.frombuffer(r[3], np.uint8) for r in rows])
|
||||
cpas = np.array([r[0] for r in rows])
|
||||
firms = np.array([ALIAS[r[1]] for r in rows])
|
||||
cpa_idx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cpa_idx[c].append(i)
|
||||
cpa_idx = {c: np.array(v) for c, v in cpa_idx.items()}
|
||||
pool_size = {c: len(v)-1 for c, v in cpa_idx.items()}
|
||||
all_idx = np.arange(n)
|
||||
print(f'BCD signatures: {n:,}; CPAs: {len(cpa_idx)}')
|
||||
|
||||
rng = np.random.default_rng(SEED)
|
||||
hit_any = np.zeros(n, bool)
|
||||
hit_same = np.zeros(n, bool)
|
||||
cand_firm_maxcos = np.empty(n, dtype=object) # any-pair partner firm
|
||||
cand_firm_same = np.empty(n, dtype=object)
|
||||
psize = np.zeros(n, np.int32)
|
||||
for si in range(n):
|
||||
np_ = pool_size[cpas[si]]; psize[si] = np_
|
||||
if np_ <= 0:
|
||||
continue
|
||||
same = cpa_idx[cpas[si]]
|
||||
need = np_; cand = []; att = 0
|
||||
while need > 0 and att < 10:
|
||||
draw = rng.choice(n, size=need*2, replace=True)
|
||||
ok = draw[~np.isin(draw, same)]
|
||||
cand.extend(ok[:need].tolist()); need -= len(ok[:need]); att += 1
|
||||
if need > 0:
|
||||
pm = np.ones(n, bool); pm[same] = False
|
||||
cand.extend(rng.choice(all_idx[pm], size=need, replace=False).tolist())
|
||||
cand = np.array(cand[:np_], dtype=np.int64)
|
||||
cosv = feats[cand] @ feats[si]
|
||||
dist = POP[dh[cand] ^ dh[si]].sum(axis=1)
|
||||
mc = int(np.argmax(cosv)); md = int(np.argmin(dist))
|
||||
if cosv[mc] > 0.95 and dist[md] <= 5:
|
||||
hit_any[si] = True
|
||||
cand_firm_maxcos[si] = firms[cand[mc]]
|
||||
spm = (cosv > 0.95) & (dist <= 5)
|
||||
if spm.any():
|
||||
hit_same[si] = True
|
||||
cand_firm_same[si] = firms[cand[int(np.argmax(spm))]]
|
||||
|
||||
# ---- Logistic regression: hit_any ~ FirmB + FirmC + log(pool), Firm D reference ----
|
||||
hp = psize > 0
|
||||
y = hit_any[hp].astype(np.float64)
|
||||
fa = firms[hp]
|
||||
lp = np.log(psize[hp].astype(np.float64)); lp = lp - lp.mean()
|
||||
X = np.column_stack([np.ones(y.shape), (fa == 'B').astype(float), (fa == 'C').astype(float), lp])
|
||||
beta, se = logistic_fit(X, y)
|
||||
print(f'\n[BCD logistic: hit_any ~ FirmB + FirmC + log(pool); Firm D = reference] n={len(y):,}, y_mean={y.mean():.4f}')
|
||||
for nm, b, s in zip(['intercept(FirmD)', 'FirmB', 'FirmC', 'log(pool,centred)'], beta, se):
|
||||
print(f' {nm}: beta={b:+.4f} SE={s:.4f} OR={np.exp(b):.4f} z~{abs(b)/s if s>0 else float("inf"):.2f}')
|
||||
|
||||
# ---- Cross-firm hit matrix (any-pair max-cos partner) ----
|
||||
print('\n[BCD cross-firm hit matrix: any-pair, source firm x max-cos partner firm]')
|
||||
print(' src -> B C D | within-firm% (n hits)')
|
||||
for sf in ['B', 'C', 'D']:
|
||||
m = (firms == sf) & hit_any
|
||||
parts = cand_firm_maxcos[m]
|
||||
tot = len(parts)
|
||||
cnt = {f: int((parts == f).sum()) for f in ['B', 'C', 'D']}
|
||||
wf = cnt[sf]/tot if tot else 0
|
||||
print(f' {sf} {cnt["B"]:5d} {cnt["C"]:5d} {cnt["D"]:5d} | {wf:6.1%} ({tot})')
|
||||
|
||||
print('\n[BCD same-pair within-firm concentration]')
|
||||
for sf in ['B', 'C', 'D']:
|
||||
m = (firms == sf) & hit_same
|
||||
parts = cand_firm_same[m]; tot = len(parts)
|
||||
wf = int((parts == sf).sum())/tot if tot else 0
|
||||
print(f' Firm {sf}: {wf:.1%} within-firm ({int((parts==sf).sum())}/{tot})')
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 54: temporal stability of the BCD inter-CPA floor.
|
||||
Does the normative BCD per-comparison HC coincidence floor drift over time /
|
||||
get contaminated by post-2020 e-signing? Compares eras full / 2013-2019 /
|
||||
2020-2023 using the pool-size-independent per-comparison joint HC ICCR
|
||||
(cos>0.95 & dHash<=5) on BCD inter-CPA pairs (N=500k, seed 42), plus the
|
||||
observed deployed per-signature HC rate by firm by era. Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
N_PAIRS = 500_000
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n
|
||||
c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT s.assigned_accountant, a.firm, CAST(substr(s.year_month,1,4) AS INT),
|
||||
s.feature_vector, s.dhash_vector,
|
||||
s.max_similarity_to_same_accountant, s.min_dhash_independent
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE a.firm IN (?,?,?,?) AND s.year_month IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
ERAS = {'full 2013-2023': lambda y: True,
|
||||
'2013-2019 (pre-drift)': lambda y: 2013 <= y <= 2019,
|
||||
'2020-2023': lambda y: 2020 <= y <= 2023}
|
||||
|
||||
|
||||
def per_comparison_floor(era_fn, label):
|
||||
# BCD-only (exclude Firm A), era-restricted
|
||||
keep = [r for r in rows if r[1] != FIRM_A and era_fn(r[2])]
|
||||
feats = np.stack([np.frombuffer(r[3], np.float32) for r in keep]).astype(np.float32)
|
||||
feats /= np.clip(np.linalg.norm(feats, axis=1, keepdims=True), 1e-9, None)
|
||||
dh = np.stack([np.frombuffer(r[4], np.uint8) for r in keep])
|
||||
cpas = np.array([r[0] for r in keep])
|
||||
by = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
by[c].append(i)
|
||||
accts = list(by.keys())
|
||||
rng = np.random.default_rng(SEED)
|
||||
cos = np.empty(N_PAIRS, np.float32); dv = np.empty(N_PAIRS, np.int32)
|
||||
na = len(accts)
|
||||
for t in range(N_PAIRS):
|
||||
i, j = rng.choice(na, 2, replace=False)
|
||||
a1, a2 = accts[i], accts[j]
|
||||
k1 = by[a1][int(rng.integers(0, len(by[a1])))]
|
||||
k2 = by[a2][int(rng.integers(0, len(by[a2])))]
|
||||
cos[t] = feats[k1] @ feats[k2]
|
||||
dv[t] = POP[dh[k1] ^ dh[k2]].sum()
|
||||
joint = int(((cos > 0.95) & (dv <= 5)).sum())
|
||||
lo, hi = wilson(joint, N_PAIRS)
|
||||
print(f' [{label}] BCD per-comparison HC floor = {joint/N_PAIRS:.6f} '
|
||||
f'({joint}/{N_PAIRS}) Wilson95% [{lo:.6f},{hi:.6f}] '
|
||||
f'(n_sig={len(keep):,}, CPAs={na})')
|
||||
return joint/N_PAIRS
|
||||
|
||||
|
||||
print('=== (1) BCD per-comparison HC floor by era (pool-size-independent) ===')
|
||||
floors = {lab: per_comparison_floor(fn, lab) for lab, fn in ERAS.items()}
|
||||
|
||||
print('\n=== (2) Observed deployed per-signature HC rate by firm by era ===')
|
||||
print(' (max_sim>0.95 & min_dh<=5 on actual same-CPA pools)')
|
||||
for lab, fn in ERAS.items():
|
||||
print(f' --- {lab} ---')
|
||||
for fm_zh in BIG4:
|
||||
sub = [r for r in rows if r[1] == fm_zh and fn(r[2])
|
||||
and r[5] is not None and r[6] is not None]
|
||||
if not sub:
|
||||
continue
|
||||
k = sum(1 for r in sub if r[5] > 0.95 and r[6] <= 5)
|
||||
print(f' Firm {ALIAS[fm_zh]}: {k/len(sub):.4f} ({k}/{len(sub)})')
|
||||
|
||||
print('\n=== A-vs-floor multiple by era (observed A HC / BCD floor) ===')
|
||||
for lab, fn in ERAS.items():
|
||||
a = [r for r in rows if r[1] == FIRM_A and fn(r[2]) and r[5] is not None and r[6] is not None]
|
||||
a_rate = sum(1 for r in a if r[5] > 0.95 and r[6] <= 5)/len(a) if a else 0
|
||||
fl = floors[lab]
|
||||
# per-comparison floor is not directly comparable to observed pooled rate;
|
||||
# report ratio vs the per-signature floor proxy from Script 52 (0.0116 full).
|
||||
print(f' {lab}: observed A HC = {a_rate:.3f}; per-comparison floor = {fl:.6f}')
|
||||
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Script 55: PRIMARY calibration on the clean pre-e-signature baseline
|
||||
BCD 2013-2019 (Firms B/C/D, fiscal years 2013-2019). Rationale: co-author
|
||||
interviews confirm B/C/D progressively adopted e-signature systems after 2020
|
||||
(staggered timing), so 2013-2019 BCD is the construct-clean hand-signing
|
||||
baseline. Canonical retry-loop sampler (matches Scripts 43/45/52), any-pair.
|
||||
Reports the floor + Firm A (all years) scored out-of-sample against it, and
|
||||
BCD 2020+ scored against the same threshold. Read-only.
|
||||
"""
|
||||
import sqlite3
|
||||
from collections import defaultdict, Counter
|
||||
import numpy as np
|
||||
|
||||
DB = '/Volumes/NV2/PDF-Processing/signature-analysis/signature_analysis.db'
|
||||
FIRM_A = '勤業眾信聯合'
|
||||
BIG4 = ('勤業眾信聯合', '安侯建業聯合', '資誠聯合', '安永聯合')
|
||||
ALIAS = {'勤業眾信聯合': 'A', '安侯建業聯合': 'B', '資誠聯合': 'C', '安永聯合': 'D'}
|
||||
SEED = 42
|
||||
N_BOOT = 1000
|
||||
POP = np.array([bin(i).count('1') for i in range(256)], dtype=np.uint8)
|
||||
|
||||
|
||||
def wilson(k, n, z=1.96):
|
||||
if n == 0:
|
||||
return (None, None)
|
||||
p = k/n; d = 1+z*z/n; c = (p+z*z/(2*n))/d
|
||||
h = z*np.sqrt(p*(1-p)/n+z*z/(4*n*n))/d
|
||||
return (max(0.0, c-h), min(1.0, c+h))
|
||||
|
||||
|
||||
def canon_sampler(rng, n, npool, same, all_idx):
|
||||
need = npool; cand = []; att = 0
|
||||
while need > 0 and att < 10:
|
||||
draw = rng.choice(n, size=need*2, replace=True)
|
||||
ok = draw[~np.isin(draw, same)]
|
||||
cand.extend(ok[:need].tolist()); need -= len(ok[:need]); att += 1
|
||||
if need > 0:
|
||||
pm = np.ones(n, bool); pm[same] = False
|
||||
cand.extend(rng.choice(all_idx[pm], size=need, replace=False).tolist())
|
||||
return np.array(cand[:npool], dtype=np.int64)
|
||||
|
||||
|
||||
conn = sqlite3.connect(f'file:{DB}?mode=ro', uri=True)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT s.assigned_accountant,a.firm,CAST(substr(s.year_month,1,4) AS INT),
|
||||
s.source_pdf,s.feature_vector,s.dhash_vector,
|
||||
s.max_similarity_to_same_accountant,s.min_dhash_independent
|
||||
FROM signatures s JOIN accountants a ON s.assigned_accountant=a.name
|
||||
WHERE a.firm IN (?,?,?,?) AND s.year_month IS NOT NULL
|
||||
AND s.feature_vector IS NOT NULL AND s.dhash_vector IS NOT NULL""", BIG4)
|
||||
rows = cur.fetchall()
|
||||
conn.close()
|
||||
|
||||
|
||||
def prep(rec):
|
||||
feats = np.stack([np.frombuffer(r[4], np.float32) for r in rec]).astype(np.float32)
|
||||
norms = np.linalg.norm(feats, axis=1, keepdims=True); norms[norms == 0] = 1.0
|
||||
feats /= norms
|
||||
dh = np.stack([np.frombuffer(r[5], np.uint8) for r in rec])
|
||||
return feats, dh
|
||||
|
||||
|
||||
def floor_on(baseline_rec, label):
|
||||
"""Canonical per-sig/per-doc HC floor on a baseline population."""
|
||||
feats, dh = prep(baseline_rec)
|
||||
n = len(baseline_rec)
|
||||
cpas = np.array([r[0] for r in baseline_rec])
|
||||
firms = np.array([ALIAS[r[1]] for r in baseline_rec])
|
||||
docs = np.array([r[3] for r in baseline_rec])
|
||||
cidx = defaultdict(list)
|
||||
for i, c in enumerate(cpas):
|
||||
cidx[c].append(i)
|
||||
cidx = {c: np.array(v) for c, v in cidx.items()}
|
||||
psize = {c: len(v)-1 for c, v in cidx.items()}
|
||||
all_idx = np.arange(n)
|
||||
rng = np.random.default_rng(SEED)
|
||||
mx = np.zeros(n, np.float32); mn = np.full(n, 64, np.int32)
|
||||
for si in range(n):
|
||||
np_ = psize[cpas[si]]
|
||||
if np_ <= 0:
|
||||
continue
|
||||
cand = canon_sampler(rng, n, np_, cidx[cpas[si]], all_idx)
|
||||
cosv = feats[cand] @ feats[si]
|
||||
mx[si] = cosv.max(); mn[si] = int(POP[dh[cand] ^ dh[si]].sum(axis=1).min())
|
||||
hc = (mx > 0.95) & (mn <= 5); d2 = (mx > 0.95) & (mn <= 15)
|
||||
k = int(hc.sum())
|
||||
rng2 = np.random.default_rng(SEED+1); cl = list(cidx.keys())
|
||||
bs = np.array([hc[np.concatenate([cidx[cl[i]] for i in rng2.choice(len(cl), len(cl), True)])].mean()
|
||||
for _ in range(N_BOOT)])
|
||||
print(f'\n [{label}] n_sig={n:,}, CPAs={len(cidx)}')
|
||||
print(f' per-sig HC floor = {k/n:.4f} ({k}/{n}) CPA-boot95% [{np.percentile(bs,2.5):.4f},{np.percentile(bs,97.5):.4f}]')
|
||||
dd1 = defaultdict(bool); dd2 = defaultdict(bool); dfirm = {}
|
||||
for i in range(n):
|
||||
if hc[i]: dd1[docs[i]] = True
|
||||
if d2[i]: dd2[docs[i]] = True
|
||||
dfirm.setdefault(docs[i], []).append(firms[i])
|
||||
dd1.setdefault(docs[i], False); dd2.setdefault(docs[i], False)
|
||||
dl = list(dd1.keys()); nd = len(dl)
|
||||
print(f' per-doc HC = {sum(dd1[d] for d in dl)/nd:.4f}; per-doc HC+MC = {sum(dd2[d] for d in dl)/nd:.4f} (n_doc={nd:,})')
|
||||
dom = {d: Counter(dfirm[d]).most_common(1)[0][0] for d in dl}
|
||||
for f in ['B', 'C', 'D']:
|
||||
ds = [d for d in dl if dom[d] == f]
|
||||
if ds:
|
||||
print(f' Firm {f} per-doc HC+MC: {sum(dd2[d] for d in ds)/len(ds):.4f} ({sum(dd2[d] for d in ds)}/{len(ds)})')
|
||||
return k/n
|
||||
|
||||
|
||||
def a_vs_baseline(baseline_rec, a_rec, label):
|
||||
bf, bdh = prep(baseline_rec); nb = len(baseline_rec)
|
||||
a_cpa = defaultdict(list)
|
||||
for i, r in enumerate(a_rec):
|
||||
a_cpa[r[0]].append(i)
|
||||
psize = {c: len(v)-1 for c, v in a_cpa.items()}
|
||||
rng = np.random.default_rng(SEED)
|
||||
hc = np.zeros(len(a_rec), bool)
|
||||
for i, r in enumerate(a_rec):
|
||||
np_ = psize[r[0]]
|
||||
if np_ <= 0:
|
||||
continue
|
||||
cand = rng.integers(0, nb, size=np_)
|
||||
sf = np.frombuffer(r[4], np.float32).astype(np.float32); sf /= max(np.linalg.norm(sf), 1e-9)
|
||||
cosv = bf[cand] @ sf
|
||||
if (cosv > 0.95).any():
|
||||
dist = POP[bdh[cand] ^ np.frombuffer(r[5], np.uint8)].sum(axis=1)
|
||||
hc[i] = bool(((cosv > 0.95) & (dist <= 5)).any())
|
||||
k = int(hc.sum()); n = len(a_rec); lo, hi = wilson(k, n)
|
||||
print(f' [{label}] Firm A (all yrs) vs BCD-2013-2019 pool: per-sig HC = {k/n:.4f} ({k}/{n}) [{lo:.5f},{hi:.5f}]')
|
||||
|
||||
|
||||
bcd_pre = [r for r in rows if r[1] != FIRM_A and 2013 <= r[2] <= 2019]
|
||||
bcd_post = [r for r in rows if r[1] != FIRM_A and r[2] >= 2020]
|
||||
A_all = [r for r in rows if r[1] == FIRM_A]
|
||||
|
||||
print('=== PRIMARY floor: BCD 2013-2019 ===')
|
||||
fl = floor_on(bcd_pre, 'BCD 2013-2019 (PRIMARY)')
|
||||
|
||||
print('\n=== Firm A scored against the BCD-2013-2019 threshold ===')
|
||||
a_vs_baseline(bcd_pre, A_all, 'A out-of-sample')
|
||||
A_obs = [r for r in A_all if r[6] is not None and r[7] is not None]
|
||||
ak = sum(1 for r in A_obs if r[6] > 0.95 and r[7] <= 5)
|
||||
print(f' Firm A observed (all yrs, own pools): per-sig HC = {ak/len(A_obs):.4f} -> {ak/len(A_obs)/fl:.0f}x the BCD-2013-2019 floor')
|
||||
|
||||
print('\n=== (optional) BCD 2020+ floor, same method (may be inflated by e-signing) ===')
|
||||
floor_on(bcd_post, 'BCD 2020-2023 (post e-signing)')
|
||||
Reference in New Issue
Block a user