Peki! Sonunda tutarlı bir şekilde çalışan bir şey elde etmeyi başardım! Bu problem beni birkaç günlüğüne çekti ... Eğlenceli şeyler! Bu cevabın uzunluğu için özür dilerim, ancak bazı konularda biraz detay vermem gerekiyor ... (Şimdiye kadarki en uzun spam olmayan yığın akışı yanıtı için bir rekor kırmış olsam da!)
Bir yan not olarak, Ivo tamamının veri kümesini kullanıyorum bir bağlantı sağlanan onun içinde asıl soruya . Her biri ascii dizileri olarak depolanan birkaç farklı deney çalışması içeren bir dizi rar dosyasıdır (köpek başına bir). Bağımsız kod örneklerini bu soruya kopyalayıp yapıştırmayı denemek yerine, işte tam, bağımsız kod içeren bir bitbucket mercurial deposu . İle klonlayabilirsiniz
hg clone https://joferkington@bitbucket.org/joferkington/paw-analysis
genel bakış
Sorunuzda belirttiğiniz gibi, soruna yaklaşmanın esasen iki yolu vardır. Aslında ikisini de farklı şekillerde kullanacağım.
- Hangi pençenin hangisi olduğunu belirlemek için pençe darbelerinin (zamansal ve uzaysal) sırasını kullanın.
- "Pençe izini" tamamen şekline göre tanımlamaya çalışın.
Temel olarak, ilk yöntem köpeğin pençeleri ile çalışır, yukarıdaki Ivo'nun sorusunda gösterilen yamuk benzeri modeli takip eder, ancak pençeler bu modeli takip etmediğinde başarısız olur. Çalışmadığında programatik olarak tespit etmek oldukça kolaydır.
Bu nedenle, hangi pençenin hangisi olduğunu anlamak için bir eğitim veri seti (~ 30 farklı köpekten ~ 2000 pençe etkisi) oluşturmak için işe yaradığı ölçümleri kullanabiliriz ve sorun denetimli bir sınıflandırmaya indirgenir (Bazı ek kırışıklıklar ile. .. Görüntü tanıma, "normal" denetimli sınıflandırma probleminden biraz daha zordur).
Örüntü Analizi
İlk yöntemi detaylandırmak için, bir köpek normalde yürürken (koşmuyor!) (Bu köpeklerden bazıları olmayabilir), pençelerin şu sırayla çarpmasını bekliyoruz: Ön Sol, Sağ Ön, Sağ Ön, Sol Arka , Ön Sol, vb. Model, ön sol veya ön sağ pençe ile başlayabilir.
Bu her zaman böyle olsaydı, etkileri basitçe ilk temas süresine göre sıralayabilir ve onları pençeye göre gruplamak için bir modulo 4 kullanabilirdik.
Ancak her şey "normal" olsa bile bu işe yaramıyor. Bunun nedeni, desenin yamuk benzeri şeklidir. Bir arka pençe uzamsal olarak önceki ön pençenin arkasına düşer.
Bu nedenle, ilk ön pençe darbesinden sonraki arka pençe darbesi genellikle sensör plakasından düşer ve kaydedilmez. Benzer şekilde, sensör plakasından önceki pençe darbesi kaydedilmediğinden, son pençe darbesi genellikle dizideki bir sonraki pençe değildir.
Bununla birlikte, bunun ne zaman gerçekleştiğini ve sol veya sağ ön pençeyle mi başladığımızı belirlemek için pençe çarpma modelinin şeklini kullanabiliriz. (Aslında buradaki son etkiyle ilgili sorunları görmezden geliyorum. Yine de eklemek çok zor değil.)
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
Tüm bunlara rağmen, çoğu zaman düzgün çalışmıyor. Tam veri kümesindeki köpeklerin çoğu koşuyor gibi görünüyor ve pençe etkileri, köpek yürürken olduğu gibi aynı zamansal sırayı takip etmiyor. (Ya da belki köpeğin sadece ciddi kalça problemleri vardır ...)
Neyse ki, pençe etkilerinin beklenen uzaysal modelimizi takip edip etmediğini programatik olarak tespit edebiliriz:
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
Bu nedenle, basit uzamsal sınıflandırma her zaman işe yaramasa da, ne zaman çalışacağını makul bir güvenle belirleyebiliriz.
Eğitim Veri Kümesi
Doğru şekilde çalıştığı modele dayalı sınıflandırmalardan, doğru sınıflandırılmış pençelerden oluşan çok büyük bir eğitim veri seti oluşturabiliriz (32 farklı köpekten ~ 2400 pençe etkisi!).
Şimdi "ortalama" bir sol ön, vb. Pençenin neye benzediğine bakmaya başlayabiliriz.
Bunu yapmak için, herhangi bir köpek için aynı boyutsallık olan bir tür "pençe ölçüsü" ne ihtiyacımız var. (Tam veri setinde hem çok büyük hem de çok küçük köpekler var!) Bir İrlanda elkhound'undan alınan bir pençe izi, oyuncak bir kanişin pençe baskısından hem çok daha geniş hem de çok "ağır" olacaktır. Her bir pençe baskısını yeniden ölçeklendirmemiz gerekir, böylece a) aynı sayıda piksele sahip olurlar ve b) basınç değerleri standartlaştırılır. Bunu yapmak için, her pençe baskısını 20x20'lik bir ızgara üzerinde yeniden örnekledim ve pençe etkisi için maksimum, minimum ve ortalama basınç değerine dayalı olarak basınç değerlerini yeniden ölçeklendirdim.
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
Tüm bunlardan sonra, nihayet ortalama bir sol ön, sağ arka vb. Pençenin neye benzediğine bir göz atabiliriz. Bunun, çok farklı boyutlarda 30'dan fazla köpek için ortalamasının alındığını ve tutarlı sonuçlar aldığımızı unutmayın!
Bununla birlikte, bunlar üzerinde herhangi bir analiz yapmadan önce, ortalamayı (tüm köpeklerin tüm bacakları için ortalama pençe) çıkarmamız gerekir.
Şimdi ortalamadan farkları analiz edebiliriz ki bu biraz daha kolay anlaşılabilir:
Görüntü tabanlı Pençe Tanıma
Tamam ... Sonunda pençeleri eşleştirmeye başlayabileceğimiz bir dizi modelimiz var. Her pençe, paw_image
bu dört 400 boyutlu vektörle karşılaştırılabilen 400 boyutlu bir vektör ( fonksiyon tarafından döndürülen ) olarak değerlendirilebilir.
Ne yazık ki, sadece "normal" denetimli bir sınıflandırma algoritması kullanırsak (yani, 4 modelden hangisinin belirli bir pençe izine en yakın olduğunu basit bir mesafe kullanarak bulursak), tutarlı bir şekilde çalışmaz. Aslında, eğitim veri setinde rastgele şanstan çok daha iyisini yapmaz.
Bu, görüntü tanımada yaygın bir sorundur. Girdi verilerinin yüksek boyutsallığı ve görüntülerin biraz "bulanık" doğası nedeniyle (yani, bitişik pikseller yüksek bir kovaryansa sahiptir), basitçe bir görüntünün bir şablon görüntüsünden farkına bakmak, çok iyi bir ölçüm vermez. şekillerinin benzerliği.
Eigenpaws
Bunu aşmak için bir dizi "öz pençe" (yüz tanımadaki "özyüzler" gibi) oluşturmalı ve her pençe izini bu öz pençelerin bir kombinasyonu olarak tanımlamalıyız. Bu, temel bileşen analiziyle aynıdır ve temelde verilerimizin boyutluluğunu azaltmanın bir yolunu sağlar, böylece mesafe iyi bir şekil ölçüsüdür.
Boyutlardan daha fazla eğitim resmine sahip olduğumuz için (2400'e karşı 400), hız için "fantezi" doğrusal cebir yapmaya gerek yoktur. Doğrudan eğitim veri setinin kovaryans matrisi ile çalışabiliriz:
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
Bunlar basis_vecs
"öz pençeler" dir.
Bunları kullanmak için, her pençe görüntüsünü (20x20 görüntü yerine 400 boyutlu bir vektör olarak) temel vektörlerle nokta (yani matris çarpımı) yaparız. Bu bize görüntüyü sınıflandırmak için kullanabileceğimiz 50 boyutlu bir vektör (temel vektör başına bir eleman) verir. 20x20'lik bir görüntüyü her "şablon" pençenin 20x20 görüntüsüyle karşılaştırmak yerine, 50 boyutlu, dönüştürülmüş görüntüyü her 50 boyutlu dönüştürülmüş şablon pençeyle karşılaştırıyoruz. Bu, her bir ayak parmağının tam olarak nasıl konumlandırıldığı vb. İle ilgili küçük değişikliklere çok daha az duyarlıdır ve temel olarak sorunun boyutluluğunu sadece ilgili boyutlara indirger.
Eigenpaw tabanlı Pençe Sınıflandırması
Şimdi, hangi pençenin hangisi olduğunu sınıflandırmak için her bir bacak için 50 boyutlu vektörler ile "şablon" vektörleri arasındaki mesafeyi kullanabiliriz:
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()]
İşte sonuçların bazıları:
Kalan Sorunlar
Hala bazı problemler var, özellikle açık bir pençe izi yapamayacak kadar küçük köpeklerde ... (Ayak parmakları sensörün çözünürlüğünde daha net bir şekilde ayrıldığından büyük köpeklerde en iyi sonucu verir.) Ayrıca, kısmi pençe izleri bununla tanınmaz. sistem, yamuk-desen tabanlı sistem ile olabilirken.
Bununla birlikte, öz-ayak analizi doğası gereği bir mesafe ölçüsü kullandığından, pençeleri her iki şekilde sınıflandırabiliriz ve öz-ayak analizinin "kod çizelgesine" olan en küçük mesafesi bir eşiğin üzerinde olduğunda yamuk model tabanlı sisteme geri dönebiliriz. Yine de bunu henüz uygulamadım.
Vay be ... Bu uzun sürdü! Böyle eğlenceli bir soru sorduğum için şapkam Ivo'ya gitti!