ठीक है! मैं अंत में लगातार कुछ काम करने में कामयाब रहा हूँ! इस समस्या ने मुझे कई दिनों तक खींच लिया ... मजेदार सामान! इस उत्तर की लंबाई के लिए खेद है, लेकिन मुझे कुछ चीजों पर थोड़ा विस्तार करने की ज़रूरत है ... (हालांकि मैं सबसे लंबे समय तक गैर-स्पैम स्टैक ओवरफ्लो उत्तर के लिए रिकॉर्ड सेट कर सकता हूं!)
एक साइड नोट के रूप में, मैं ' मैं अपने original question में Ivo provided a link to पूर्ण डेटासेट का उपयोग कर रहा हूं। यह रार फाइलों (एक-प्रति-कुत्ते) की एक श्रृंखला है जिसमें प्रत्येक कई प्रयोगों को एसीआई एरे के रूप में संग्रहीत किया जाता है। इस प्रश्न में स्टैंड-अलोन कोड उदाहरणों को कॉपी-पेस्ट करने की बजाय, यहां एक bitbucket mercurial repository पूर्ण, स्टैंड-अलोन कोड है। आप
hg clone https://[email protected]/joferkington/paw-analysis
अवलोकन
के साथ क्लोन कर सकते हैं अनिवार्य रूप समस्या दृष्टिकोण के दो तरीके हैं, के रूप में आप अपने प्रश्न में उल्लेख किया। मैं वास्तव में दोनों अलग-अलग तरीकों से उपयोग करने जा रहा हूं।
- पाउ प्रभावों के (अस्थायी और स्थानिक) क्रम का उपयोग यह निर्धारित करने के लिए करें कि कौन सा पंजा है।
- अपने आकार पर पूरी तरह से आधारित "पैप्रप्रिंट" की पहचान करने का प्रयास करें।
असल में, पहली विधि काम करता है कुत्ते के पंजे के साथ समलम्बाकार की तरह पैटर्न ऊपर इवो के सवाल में दिखाया गया है का पालन करें, लेकिन विफल रहता है जब भी पंजे उस पैटर्न का पालन नहीं करते। यह काम नहीं करता है जब प्रोग्रामिक रूप से पता लगाने के लिए काफी आसान है।
इसलिए, हम उन मापों का उपयोग कर सकते हैं जहां उन्होंने एक प्रशिक्षण डेटासेट (~ 30 विभिन्न कुत्तों से ~ 2000 पंजा प्रभावों का निर्माण) बनाने के लिए काम किया था, यह पहचानने के लिए कि कौन सा पंजा है, और समस्या पर्यवेक्षित वर्गीकरण को कम करती है कुछ अतिरिक्त झुर्री ... छवि पहचान एक "सामान्य" पर्यवेक्षित वर्गीकरण समस्या से थोड़ा कठिन है)।
पैटर्न विश्लेषण
पहली विधि पर विस्तार से बता दें जब एक कुत्ता चल रहा है (नहीं चल रहा है!) सामान्य रूप से (इन कुत्तों से कुछ नहीं हो सकता है), हम पंजे में प्रभावित करने की उम्मीद का आदेश: फ्रंट बाएं, हिंद राइट, फ्रंट राइट, हिंद वाम, फ्रंट बाएं इत्यादि। पैटर्न या तो सामने वाले बाएं या सामने दाएं पंजे से शुरू हो सकता है।
यदि यह हमेशा मामला होता है, तो हम प्रारंभिक संपर्क समय से प्रभाव को आसानी से सॉर्ट कर सकते हैं और पंजा द्वारा उन्हें समूहित करने के लिए मॉड्यूल 4 का उपयोग कर सकते हैं।
हालांकि, यहां तक कि जब सब कुछ "सामान्य" है, यह काम नहीं करता। यह पैटर्न के trapezoid जैसे आकार के कारण है। एक पिछला पंजा पिछले मोर्चे के पीछे स्थानिक रूप से गिरता है।
इसलिए, शुरुआती फ्रंट पंजा प्रभाव के बाद हिंद पंजा प्रभाव अक्सर सेंसर प्लेट से गिर जाता है, और रिकॉर्ड नहीं किया जाता है। इसी तरह, आखिरी पंजा प्रभाव अक्सर अनुक्रम में अगला पंजा नहीं होता है, क्योंकि इससे पहले सेंसर प्लेट से निकलने से पहले पंजा प्रभाव पड़ता है और रिकॉर्ड नहीं किया जाता है।
बहरहाल, हम पंजा प्रभाव पैटर्न के आकार का उपयोग यह निर्धारित करने के लिए जब यह हो गया है सकते हैं, और हम एक बाईं या दाईं सामने पंजा के साथ शुरू कर दिया है या नहीं। इस सब के बावजूद (मैं वास्तव में, पिछले प्रभाव यहाँ के साथ समस्याओं की अनदेखी कर रहा हूँ। यह इसे जोड़ने के लिए भी मुश्किल नहीं है, हालांकि।)
def group_paws(data_slices, time):
# Sort slices by initial contact time
data_slices.sort(key=lambda s: s[-1].start)
# Get the centroid for each paw impact...
paw_coords = []
for x,y,z in data_slices:
paw_coords.append([(item.stop + item.start)/2.0 for item in (x,y)])
paw_coords = np.array(paw_coords)
# Make a vector between each sucessive impact...
dx, dy = np.diff(paw_coords, axis=0).T
#-- Group paws -------------------------------------------
paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
paw_number = np.arange(len(paw_coords))
# Did we miss the hind paw impact after the first
# front paw impact? If so, first dx will be positive...
if dx[0] > 0:
paw_number[1:] += 1
# Are we starting with the left or right front paw...
# We assume we're starting with the left, and check dy[0].
# If dy[0] > 0 (i.e. the next paw impacts to the left), then
# it's actually the right front paw, instead of the left.
if dy[0] > 0: # Right front paw impact...
paw_number += 2
# Now we can determine the paw with a simple modulo 4..
paw_codes = paw_number % 4
paw_labels = [paw_code[code] for code in paw_codes]
return paw_labels
, यह अक्सर सही ढंग से काम नहीं करता है। पूर्ण डेटासेट में से कई कुत्ते चल रहे प्रतीत होते हैं, और जबड़े कुत्ते चलते हैं तो पंजा प्रभाव उसी अस्थायी क्रम का पालन नहीं करते हैं। सौभाग्य से (या शायद कुत्ते सिर्फ गंभीर कूल्हे समस्या है ...)
, हम अभी भी प्रोग्राम के पता लगा सकते हैं या नहीं, पंजा प्रभावों हमारी उम्मीद स्थानिक पैटर्न का पालन करें:
def paw_pattern_problems(paw_labels, dx, dy):
"""Check whether or not the label sequence "paw_labels" conforms to our
expected spatial pattern of paw impacts. "paw_labels" should be a sequence
of the strings: "LH", "RH", "LF", "RF" corresponding to the different paws"""
# Check for problems... (This could be written a _lot_ more cleanly...)
problems = False
last = paw_labels[0]
for paw, dy, dx in zip(paw_labels[1:], dy, dx):
# Going from a left paw to a right, dy should be negative
if last.startswith('L') and paw.startswith('R') and (dy > 0):
problems = True
break
# Going from a right paw to a left, dy should be positive
if last.startswith('R') and paw.startswith('L') and (dy < 0):
problems = True
break
# Going from a front paw to a hind paw, dx should be negative
if last.endswith('F') and paw.endswith('H') and (dx > 0):
problems = True
break
# Going from a hind paw to a front paw, dx should be positive
if last.endswith('H') and paw.endswith('F') and (dx < 0):
problems = True
break
last = paw
return problems
इसलिए , भले ही सरल स्थानिक वर्गीकरण हर समय काम नहीं करता है, हम यह निर्धारित कर सकते हैं कि यह उचित आत्मविश्वास के साथ कब काम करता है।
प्रशिक्षण डेटासेट
पैटर्न आधारित वर्गीकरण जहां इसे सही ढंग से काम किया है, हम सही ढंग से वर्गीकृत पंजे की एक बहुत बड़ी प्रशिक्षण डाटासेट का निर्माण कर सकते हैं से (~ 32 विभिन्न कुत्तों से 2400 पंजा प्रभावों!)।
अब हम देखना चाहते हैं कि "औसत" फ्रंट बाएं, आदि, पंजा कैसा दिखता है।
ऐसा करने के लिए, हमें किसी भी प्रकार के "पंजा मेट्रिक" की आवश्यकता होती है जो किसी भी कुत्ते के लिए समान आयाम है। (पूर्ण डेटासेट में, बहुत बड़े और बहुत छोटे कुत्ते दोनों हैं!) एक आयरिश elkhound से एक पंजा प्रिंट एक खिलौना पूडल से एक पंजा प्रिंट से अधिक व्यापक और अधिक "भारी" होगा। हमें प्रत्येक पंजा प्रिंट को पुन: सहेजने की आवश्यकता है ताकि ए) उनके पास पिक्सल की संख्या समान हो, और बी) दबाव मान मानकीकृत हैं। ऐसा करने के लिए, मैंने प्रत्येक पंजा प्रिंट को 20x20 ग्रिड पर दोबारा लगाया और पंजा प्रभाव के लिए अधिकतम, मिनिनम और औसत दबाव मूल्य के आधार पर दबाव मूल्यों को रद्द कर दिया।
def paw_image(paw):
from scipy.ndimage import map_coordinates
ny, nx = paw.shape
# Trim off any "blank" edges around the paw...
mask = paw > 0.01 * paw.max()
y, x = np.mgrid[:ny, :nx]
ymin, ymax = y[mask].min(), y[mask].max()
xmin, xmax = x[mask].min(), x[mask].max()
# Make a 20x20 grid to resample the paw pressure values onto
numx, numy = 20, 20
xi = np.linspace(xmin, xmax, numx)
yi = np.linspace(ymin, ymax, numy)
xi, yi = np.meshgrid(xi, yi)
# Resample the values onto the 20x20 grid
coords = np.vstack([yi.flatten(), xi.flatten()])
zi = map_coordinates(paw, coords)
zi = zi.reshape((numy, numx))
# Rescale the pressure values
zi -= zi.min()
zi /= zi.max()
zi -= zi.mean() #<- Helps distinguish front from hind paws...
return zi
इस सब के बाद, हम अंत में क्या एक औसत बाईं सामने, हिंद सही, आदि पंजा तरह लग रहा है पर एक नज़र ले जा सकते हैं। ध्यान दें कि यह औसत> विभिन्न कुत्तों के 30 कुत्तों में औसत है, और हमें लगातार परिणाम मिल रहे हैं!
हालांकि, इससे पहले कि हम इन पर किसी भी विश्लेषण करते हैं, हम मतलब (सभी कुत्तों के सभी पैरों के लिए औसत पंजा) घटाना की जरूरत है।
अब हम माध्य से मतभेद है, जो थोड़ा आसान पहचान करने के लिए कर रहे हैं analyize कर सकते हैं:
छवि आधारित पंजा मान्यता
ठीक है .. अंततः हमारे पास पैटर्न का एक सेट है जिसे हम पंजे से मेल खाने का प्रयास करना शुरू कर सकते हैं। प्रत्येक पंजा एक 400 आयामी वेक्टर (paw_image
समारोह से वापस लौटे) है कि इन चार 400 आयामी वैक्टर की तुलना में किया जा सकता है के रूप में माना जा सकता है।
दुर्भाग्य से, अगर हम केवल "सामान्य" पर्यवेक्षित वर्गीकरण एल्गोरिदम का उपयोग करते हैं (यानी 4 पैटर्न में से कौन सा पैटर्न एक साधारण दूरी का उपयोग करके किसी विशेष पंजा प्रिंट के निकटतम है), यह लगातार काम नहीं करता है। वास्तव में, यह प्रशिक्षण डेटासेट पर यादृच्छिक मौका से कहीं ज्यादा बेहतर नहीं करता है।
इस छवि को मान्यता में एक आम समस्या है। इनपुट डेटा के उच्च आयामी स्वरूप, और छवियों के लिए कुछ हद तक "फजी" प्रकृति के कारण (यानी आसन्न पिक्सल एक उच्च सहप्रसरण है), बस एक टेम्पलेट छवि से एक छवि के अंतर को देखने का एक बहुत अच्छा उपाय नहीं देता उनके आकार की समानता।
Eigenpaws
इस हम "eigenpaws" (बस "Eigenfaces" चेहरे की पहचान में) की तरह का एक सेट का निर्माण, और इन eigenpaws का एक संयोजन के रूप में प्रत्येक पंजा प्रिंट वर्णन करने के लिए की जरूरत है चारों ओर पाने के लिए। यह मुख्य घटक विश्लेषण के समान है, और मूल रूप से हमारे डेटा की आयाम को कम करने का एक तरीका प्रदान करता है, ताकि दूरी आकार का एक अच्छा उपाय हो।
क्योंकि हम आयाम (2400 बनाम 400) की तुलना में अधिक प्रशिक्षण छवियों, वहाँ गति के लिए "फैंसी" रेखीय बीजगणित करने के लिए कोई ज़रूरत नहीं है। हम सीधे प्रशिक्षण डेटा सेट के सहप्रसरण मैट्रिक्स के साथ काम कर सकते हैं:
def make_eigenpaws(paw_data):
"""Creates a set of eigenpaws based on paw_data.
paw_data is a numdata by numdimensions matrix of all of the observations."""
average_paw = paw_data.mean(axis=0)
paw_data -= average_paw
# Determine the eigenvectors of the covariance matrix of the data
cov = np.cov(paw_data.T)
eigvals, eigvecs = np.linalg.eig(cov)
# Sort the eigenvectors by ascending eigenvalue (largest is last)
eig_idx = np.argsort(eigvals)
sorted_eigvecs = eigvecs[:,eig_idx]
sorted_eigvals = eigvals[:,eig_idx]
# Now choose a cutoff number of eigenvectors to use
# (50 seems to work well, but it's arbirtrary...
num_basis_vecs = 50
basis_vecs = sorted_eigvecs[:,-num_basis_vecs:]
return basis_vecs
ये basis_vecs
"eigenpaws" कर रहे हैं।
इन का उपयोग करने के लिए, हम केवल डॉट (अर्थात आव्यूह गुणन) प्रत्येक पंजा छवि आधार वैक्टर के साथ (एक 400 आयामी वेक्टर के बजाय एक 20x20 छवि के रूप में)। यह हमें 50-आयामी वेक्टर (एक आधार प्रति आधार वेक्टर) देता है जिसे हम छवि वर्गीकृत करने के लिए उपयोग कर सकते हैं। प्रत्येक "टेम्पलेट" पंजा की 20x20 छवि के लिए एक 20x20 छवि की तुलना करने के बजाय, हम एक 50-आयामी तब्दील टेम्पलेट पंजा करने के लिए 50 आयामी, तब्दील छवि की तुलना करें। यह आदि वास्तव में कैसे प्रत्येक पैर की अंगुली स्थिति में है में बहुत कम छोटे बदलाव के प्रति संवेदनशील है, और मूल रूप से सिर्फ प्रासंगिक आयामों को समस्या का आयामी स्वरूप कम करता है।
Eigenpaw आधारित पंजा वर्गीकरण
अब हम बस 50 आयामी वैक्टर और "टेम्पलेट" वैक्टर के बीच की दूरी प्रत्येक पैर के लिए वर्गीकृत करने के लिए जो पंजा है उपयोग कर सकते हैं जो:
codebook = np.load('codebook.npy') # Template vectors for each paw
average_paw = np.load('average_paw.npy')
basis_stds = np.load('basis_stds.npy') # Needed to "whiten" the dataset...
basis_vecs = np.load('basis_vecs.npy')
paw_code = {0:'LF', 1:'RH', 2:'RF', 3:'LH'}
def classify(paw):
paw = paw.flatten()
paw -= average_paw
scores = paw.dot(basis_vecs)/basis_stds
diff = codebook - scores
diff *= diff
diff = np.sqrt(diff.sum(axis=1))
return paw_code[diff.argmin()]
:
यहाँ परिणामों में से कुछ हैं 363,210
समस्याएं शेष
अब भी कुछ समस्याओं, विशेष रूप से कुत्तों भी एक स्पष्ट Pawprint बनाने के लिए छोटे के साथ कर रहे हैं ... (यह सबसे अच्छा बड़े कुत्तों के साथ, के रूप में पैर की उंगलियों और अधिक स्पष्ट रूप सेंसर के रिज़ोलुशन में अलग कर रहे हैं काम करता है।) इसके अलावा, आंशिक पाप्रप्रिंट इस प्रणाली के साथ मान्यता प्राप्त नहीं हैं, जबकि वे ट्राइपोज़ाइडल-पैटर्न-आधारित सिस्टम के साथ हो सकते हैं।
हालांकि, क्योंकि ईजिनपा विश्लेषण स्वाभाविक रूप से दूरी मीट्रिक का उपयोग करता है, इसलिए हम पंजा के दोनों तरीकों को वर्गीकृत कर सकते हैं, और ट्राइपोज़ाइडल-पैटर्न-आधारित सिस्टम पर वापस आ सकते हैं जब "कोडबुक" से ईगेंपा विश्लेषण की छोटी दूरी कुछ सीमा से अधिक हो जाती है । हालांकि, मैंने इसे अभी तक लागू नहीं किया है।
पुhew ... वह लंबा था! इस तरह के एक मजेदार सवाल के लिए मेरी टोपी आईवो के पास है!
यह आपके पिछले प्रश्न के जवाबों को पढ़ने में आकर्षक था। उम्मीद है कि यह भी बहुत रुचि पैदा करेगा। – neil
हाँ, जिस दृष्टिकोण का मैं उपयोग कर रहा था वह काफी काम नहीं करता है। बस विस्तृत करने के लिए, जिस दृष्टिकोण का मैं उपयोग कर रहा था वह सिर्फ प्रभावों का आदेश देना है, और मान लें कि स्पर्श करने वाला पहला पंजा स्पर्श करने के लिए 5 वें पंजा जैसा ही है, और इसी तरह। (यानी प्रभावों का आदेश दें और एक मॉड्यूल 4 का उपयोग करें)। इसके साथ समस्या यह है कि कभी-कभी पीछे के पंजे पहले पंजे को छूने के बाद सेंसर पैड से प्रभावित होते हैं। उस स्थिति में, प्रभाव के लिए पहला पंजा 4 वें या तीसरे पंजा से प्रभावित होने के लिए मेल खाता है। उम्मीद है कि यह कुछ समझ में आता है। –
हाँ मैंने ध्यान दिया कि जब मैंने पंजे @ जो के साथ प्रभावों को मैन्युअल रूप से एनोटेट करना शुरू किया था, लेकिन फिर भी आपकी विधि एक प्रबंधित फैशन में पंजे निकालने के लिए शानदार थी। अब मैं उम्मीद कर रहा हूं कि कोई उन्हें कुछ सॉर्ट करने के लिए भयानक हो सकता है :-) –