การใช้งาน PyWavelets CWT
ฉันพยายามที่จะทำความเข้าใจการใช้งานการแปลงคลื่นต่อเนื่องของPyWaveletsและวิธีเปรียบเทียบกับเวอร์ชัน 'พื้นฐาน' ที่ฉันได้เขียนโค้ดและให้ไว้ที่นี่ โดยเฉพาะอย่างยิ่ง:
- เวฟเล็ตแบบอินทิเกรตเป็นอย่างไรจากนั้นโพสต์คอนเวอเรนซ์แตกต่างกันอย่างไรเทียบเท่ากับการชักโดยไม่มี int + diff
- การสุ่มตัวอย่างเวฟเล็ตแบบรวมที่เพิ่มความละเอียดเทียบเท่ากับการเปลี่ยนสเกลอย่างไร
การใช้งานที่ไร้เดียงสา :
def cwt(x, win_len=None, win='morlet', real=False):
N = len(x)
win_len = win_len or N // 8
scales = _scales(N, nv=32)
coef = np.zeros((len(scales), N), dtype='complex128')
kernel = morlet_kernel(win_len)
wl2 = win_len // 2
for i, scale in enumerate(scales):
coef[i, :] = np.convolve(x, kernel(scale)[::-1])[wl2:-(wl2 - 1)]
return coef
PyWavelets (ตัดกับส่วนที่เกี่ยวข้อง) :
def pywt_cwt(data, scales):
out = np.empty((np.size(scales),) + data.shape)
int_psi, x = integrate_wavelet(wavelet='morl', precision=10)
for i, scale in enumerate(scales):
step = x[1] - x[0]
j = np.arange(scale * (x[-1] - x[0]) + 1) / (scale * step)
j = j.astype(int) # floor
if j[-1] >= int_psi.size:
j = np.extract(j < int_psi.size, j)
int_psi_scale = int_psi[j][::-1]
conv = np.convolve(data, int_psi_scale)
coef = - np.sqrt(scale) * np.diff(conv, axis=-1)
d = (coef.shape[-1] - data.shape[-1]) / 2.
coef = coef[..., floor(d):-ceil(d)]
out[i, ...] = coef
return out
การเปรียบเทียบบน $f=1, 4$ไซนัส :
คำตอบ
ขั้นแรกให้ดู "รายละเอียดที่ไร้เดียงสา" ในส่วนด้านล่าง บน PyWavelets: พบอัลกอริทึมบนGithub ซึ่งเกิดจากการใช้งาน MATLAB แบบเก่าแต่ไม่มีรายละเอียดเกี่ยวกับการเข้ารหัสเคอร์เนลเวฟเล็ต ดังนั้นฉันจึงออกสำรวจ ภาพและคำอธิบายประกอบรหัสต่างๆที่นี่ การค้นพบที่สำคัญเป็นไปตาม - รหัสทั้งหมด
ดู "รายละเอียด PyWavelets" ด้านล่าง กำลังดำเนินการต่อไปนี้ - คือ Eq 4:
(1) เวฟเล็ตแบบบูรณาการเป็นอย่างไรแล้วโพสต์คอนเวอลั่มที่แตกต่างกันอย่างไรเทียบเท่ากับการชักโดยไม่มี int + diff
สูตรอยู่ทางขวามาจากสมการทางซ้าย แทนที่จะรวมอย่างต่อเนื่องทั้งหมด$\mathcal{R}$อินทิกรัลถูกแบ่งออกเป็นเซกเมนต์ ssumming มากกว่า $k$. อินพุตจะถือว่าคงที่มากกว่า$[k, k+1]$ (แม้ว่าจะไม่ควรก็ตาม $)$?) เนื่องจากไม่ต่อเนื่องในขณะที่เวฟเล็ตจะยังคงต่อเนื่อง สิ่งนี้ทำให้สามารถย้ายอินพุตออกนอกอินทิกรัล สายตา:
แล้วเราจะเขียนโค้ดนี้อย่างไร? จาก Eq 4 ชัดเจนว่าเราต้องการdiffที่ไหนสักแห่ง pywtไม่np.diff(conv)ซึ่งโดยพื้นฐานnp.diff(coef)แล้ว ทำไมค่าสัมประสิทธิ์ที่จะเป็นจริงใน Naive จึงแตกต่างกัน
ใน Eq 4 เรามีเวฟเล็ตแบบบูรณาการ up-to k + 1, ลบ up-to k, คูณกับเวฟเล็ต ซึ่งเทียบเท่ากับผลิตภัณฑ์ที่มีเวฟที่b - 1และbตามลำดับ (เสียบปลั๊กt=k+1และt=kเปรียบเทียบกับการเสียบปลั๊กb=b-1และb=bแทน ")
คำตอบแล้วในconv; สมมติว่ามันถูกตัดแต่งไปแล้วก็ต่อเมื่อเวฟเล็ตอยู่ในสัญญาณอย่างน้อยครึ่งหนึ่ง จากนั้นทุกจุดในconvคือผลคูณของเวฟเล็ตที่มีส่วนของอินพุตที่ต่างกันtau(หรือมากกว่าb) ดังนั้นสองจุดที่อยู่ติดกันconvคือผลิตภัณฑ์ที่มีเวฟเล็ตที่bและb + 1:
$$ \begin{align} \text{conv} &= [s \cdot \psi_{\text{int}}(t - 0),\ s \cdot \psi_{\text{int}}(t - 1),\ ...] \\ \Rightarrow \text{DIFF}(\text{conv}) &= [s \cdot (\psi_{\text{int}}(t - 1) - \psi_{\text{int}}(t - 0)),\ ...] \end{align} $$
อย่างไรก็ตามเราหาค่าลบระหว่างt=k+1และtหรือมากกว่าtและต่ำกว่าtซึ่งเป็นค่าลบของข้างต้นดังนั้นจึงอธิบายขั้นตอนที่ 7 ใน PW Breakdown
(2) วิธีการสุ่มตัวอย่างเวฟเล็ตในตัวที่ความละเอียดเพิ่มขึ้นเทียบเท่ากับการเปลี่ยนสเกลอย่างไร
ดูรูปภายใต้ (5) ใน PW Breakdown pywtดูเหมือนจะ "ปรับขนาด" ผ่านจำนวนตัวอย่างที่กำหนดเวฟเล็ตแทนที่จะคำนวณเวฟเล็ตใหม่ด้วยอาร์กิวเมนต์แบบขยาย ... แม้ว่าจะเห็นได้ชัดว่าการสุ่มตัวอย่างใหม่นี้จะทำให้เวฟเล็ตขยายตัวได้อย่างชัดเจน
ดังนั้นมันจึงเป็นรูปแบบหนึ่งของการปรับขนาด แต่มันเปรียบเทียบกับความไร้เดียงสาได้อย่างไร? Naive ถูกคำนวณใหม่โดยสังหรณ์ใจมากขึ้นต่อสมการโดยใช้ความยาวเวฟเล็ตคงที่และส่งผ่านอาร์กิวเมนต์ที่ปรับขนาด แล้วแบบไหนดีกว่ากัน? คำถามตรงข้ามชัดเจนกว่าที่จะตอบ พิจารณาสิ่งที่เกิดขึ้นในระดับที่สูงขึ้นและต่ำลง:
- ไร้เดียงสาสูงขึ้น : เวฟเล็ตขยายตัวมากเกินไปโดยออกจากกรอบการสุ่มตัวอย่างไกลก่อนหางศูนย์
- pywt สูงขึ้น : เวฟเล็ตก็ดี; ปัญหากับ
conv. ความยาวเวฟเล็ตถูกกำหนดไว้ที่ 1024 ดังนั้นหากอินพุตสั้นลงเวฟเล็ตที่มีสเกลสูงกว่าจะไม่สามารถคูณสัญญาณได้เต็มที่ ยิ่งความเหลื่อมล้ำมากเท่าไหร่คลื่นก็ยิ่ง "เห็น" คล้ายกับสัญญาณ "ไร้เดียงสาสูงกว่า" มากขึ้นเท่านั้น สิ่งนี้สามารถเห็นได้ในแผนที่ความร้อนของคำถามที่แตกต่างกันไปตามการเลื่อนแนวตั้ง- สเกลสูงสุดของเวฟยังไม่ จำกัด
len(j)เติบโตตามสัดส่วนตามขนาด (แต่มีปัญหาในการทำให้เป็นมาตรฐานโปรดดูที่นี่ )
- สเกลสูงสุดของเวฟยังไม่ จำกัด
- ต่ำกว่าไร้เดียงสา : ดีทั้งหมด
- pywt ต่ำกว่า : เวฟเล็ตมีความละเอียดน้อย
- คงเทียบกับระยะเวลาในหน้าต่างตัวแปร : เครื่องชั่งน้ำหนักที่สูงขึ้นจะต้องสอดคล้องกับการขยายตัวมากขึ้นหรือเป็น-ภัณฑ์ของเวฟเทียบกับการป้อนข้อมูล ทั้งสองทำสิ่งนี้สำเร็จยกเว้น Naive หยุดเป็นศูนย์ทันทีที่สเกลที่สูงขึ้น
ปัญหาระดับที่สูงขึ้นในทั้งสองได้รับการแก้ไขโดยการ จำกัด มาตราส่วนสูงสุด หากทั้งสองอยู่ในเขตปลอดภัยอย่างสมบูรณ์การใช้งานแบบไร้เดียงสาดูเหมือนจะดีกว่าเนื่องจากมีความละเอียดสูงกว่า อย่างไรก็ตาม "เขตปลอดภัย" สำหรับ Naive นี้มีขนาดเล็กมากหากเราพิจารณาว่า "ปลอดภัย" ไม่ใช่กระโดดไปที่ศูนย์ที่หาง
ในทางตรงกันข้ามpywtปัญหาจะแก้ไขได้ง่ายขึ้นโดยการเพิ่มความยาวเวฟเล็ตสูงสุดซึ่งแนะนำให้กำจัดสิ่งประดิษฐ์ที่มีลักษณะคล้ายซิป (ซึ่งสามารถดูได้ในพล็อตคำถาม) อย่างไรก็ตามนั่นทำให้ประสิทธิภาพที่สูงขึ้นสำหรับอินพุตสั้น ๆ แย่ลง แต่ในทางปฏิบัติไม่น่าจะสั้นกว่า 1024
รายละเอียด PyWavelets :
- Wavelet ก่อนการรวมจะตรงกับโค้ดหยดที่แสดงซึ่งเป็นการประมาณค่า Morlet จริงที่สมบูรณ์ (ใช้โดย Naive) โดยสมมติว่า $\sigma > 5$ในวิกิพีเดีย
pywtรวม Morlet จริงผ่านการnp.cumsum(psi) * stepบัญชีสำหรับขนาดขั้นตอนที่แตกต่างกัน- เวฟเล็ต
int_psiในตัวถูกนำกลับมาใช้ใหม่สำหรับทุกคนscales - สำหรับแต่ละ
scaleรายการint_psiจะมีการสุ่มตัวอย่างแบบเดียวกันที่ความละเอียดที่เพิ่มขึ้นโดยjจำนวนเต็มจะแปรผันตามเส้นตรงระหว่างค่าต่ำสุดและสูงสุดเท่ากัน (เกือบ) สำหรับทั้งหมดscales:
- เวฟเล็ตที่เป็นผลลัพธ์สำหรับการเพิ่มขึ้น
scaleเคียงข้างกันอย่างไร้เดียงสา (ในภายหลัง):
- ผลการแปลงครั้งแรก
diff'd, - เมื่อตะกี้
- คูณด้วย
sqrt(scale), - แล้วตัดแต่งทั้งสองด้านอย่างใดอย่างหนึ่ง (ก) เท่านั้นรวมถึงชิ้นส่วนที่เวฟเป็นอย่างน้อยครึ่งหนึ่ง "ภายในสัญญาณ" หรือ (ข)
len(x)บังคับให้ระยะเวลาในการ
พังทลายหน่อมแน้ม : ถ้าไม่คุ้นเคยกับ CWT / STFT ผมขอแนะนำส่วนที่ 1-3 ที่นี่
เราต้องการ (1) ประเภทเวฟ; (2) ความยาวเวฟ; (3) การเพิ่มขึ้นของเวฟเล็ต / การทับซ้อนกัน เราจะใช้Morletจริง96 ตัวอย่างและเพิ่มทีละ 20
win_len = 96 # "win" == window == wavelet
win_inc = 20
n_wins = (len(x) - win_len) // win_inc + 1
coef = np.zeros((n_wins, len(scales)), dtype='complex128')
kernel = morlet_kernel(win_len)
for tau in range(n_wins):
start = tau * win_inc
end = start + win_len
coef[tau, :] = _transform(x[start:end], kernel, scales)
def _transform(x, kernel, scales):
coef = np.zeros(len(scales), dtype='complex128')
for i, scale in enumerate(scales):
psi = np.conj(kernel(scale))
coef[i] = np.sum(x * psi / np.sqrt(scale))
return coef
แสดงภาพสำหรับscale=2:
ความแตกต่างระหว่างการใช้งานแบบนี้กับการใช้งานแบบไร้เดียงสาในคำถามคือการใช้งานในช่วงหลังwin_inc=1เทียบเท่ากับการปรับเปลี่ยนด้วยเวฟเล็ตแบบพลิกและการตัดแต่งผลลัพธ์ให้รวมเฉพาะส่วนที่เวฟเล็ตเต็ม นอกจากนี้ที่นี่เรามีtau("Increment") ในวงนอกมีมาตราส่วน แต่ทั้งสองเท่ากัน
สุดท้ายรูปร่างตามไทม์ชิฟสลัวแตกต่างกัน การดำเนินการของคำถามจดจ้องเพื่อให้มีการรวมเฉพาะบางส่วนของคอนโวลูชั่นที่เวฟเล็ตอย่างน้อยครึ่งหนึ่ง "อยู่ในอินพุต" อันนี้จดจ้องไปที่ที่รวมเวฟเล็ตแบบเต็ม
ยังไม่ได้แก้ไข :
ทำไมแก้ไขแล้ว* sqrt(scale)(ขั้นตอนที่ 8 pywt)? สมการที่ 41 / sqrt(scale)แสดงให้เห็นว่า ฉันพิจารณา* scaleตามกฎลูกโซ่ยกเว้นว่าเราได้รวมเวฟเล็ตเป็นตัวเลข ... สมเหตุสมผลที่จะมีการรักษาต่อสเกล เราจะหลีกเลี่ยงการไม่รวมเวฟเล็ตต่อสเกลได้หรือไม่เพราะเวฟเล็ตต่อสเกลคือ ... เวฟเล็ตที่สุ่มตัวอย่างเหมือนกัน?