whisper.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import os
  2. import argparse
  3. from openai import OpenAI
  4. from dotenv import load_dotenv
  5. import tiktoken
  6. from pypinyin import pinyin, Style
  7. import jieba
  8. load_dotenv('environment.env')
  9. client = OpenAI()
  10. ############ 以語意做調整 ################
  11. system_prompt = """你是一位專業的轉錄校對助理,專門處理有關溫室氣體、碳排放和碳管理的對話轉錄。
  12. 你的任務是:
  13. 1. 確保以下專業術語的準確性:溫室氣體、碳排放、碳管理、碳盤查、碳權交易、碳足跡、淨零排放、碳權。
  14. 2. 在必要時添加適當的標點符號,如句號、逗號
  15. 3. 使用台灣的繁體中文,確保語言表達符合台灣的用語習慣。
  16. 4. 只更正明顯的錯誤或改善可讀性,不要改變原文的意思或結構。
  17. 5. 不要回答問題、解釋概念或添加任何不在原文中的信息。
  18. 6. 如果原文是一個問句,保持它的問句形式,不要提供答案。
  19. 請只根據提供的原文進行必要的更正,不要添加或刪除任何實質性內容。在修正時,請特別注意上下文,確保修正後的詞語符合整句話的語境。"""
  20. def num_tokens_from_string(string: str, encoding_name: str) -> int:
  21. encoding = tiktoken.get_encoding(encoding_name)
  22. num_tokens = len(encoding.encode(string))
  23. return num_tokens
  24. def transcribe(audio_file):
  25. try:
  26. transcript = client.audio.transcriptions.create(
  27. file=audio_file,
  28. model="whisper-1",
  29. response_format="text"
  30. )
  31. return transcript
  32. except Exception as e:
  33. print(f"轉錄時發生錯誤:{str(e)}")
  34. return None
  35. def process_audio_file(file_path):
  36. try:
  37. with open(file_path, "rb") as audio_file:
  38. file_size = os.path.getsize(file_path) / (1024 * 1024) # 轉換為 MB
  39. if file_size > 25:
  40. print(f"警告:文件 {os.path.basename(file_path)} 大小為 {file_size:.2f} MB,超過了 25 MB 的限制。可能無法處理。")
  41. print(f"\n處理文件:{os.path.basename(file_path)}")
  42. raw_transcript = transcribe(audio_file)
  43. if raw_transcript is None:
  44. return
  45. print("\n原始轉錄:")
  46. print(raw_transcript)
  47. corrected_transcript = post_process_transcript(raw_transcript)
  48. print("\n修正後的轉錄:")
  49. print(corrected_transcript)
  50. except Exception as e:
  51. print(f"處理文件 {os.path.basename(file_path)} 時發生錯誤:{str(e)}")
  52. def process_folder(folder_path):
  53. processed_files = 0
  54. for filename in os.listdir(folder_path):
  55. if filename.endswith((".mp3", ".wav", ".m4a")):
  56. file_path = os.path.join(folder_path, filename)
  57. process_audio_file(file_path)
  58. processed_files += 1
  59. print("\n=== 總結 ===")
  60. print(f"處理的文件數:{processed_files}")
  61. ############ 以諧音做調整 #################
  62. def chinese_soundex(pinyin):
  63. # 簡化的中文拼音Soundex映射
  64. soundex_map = {
  65. 'b': '1', 'p': '1', 'm': '1', 'f': '1',
  66. 'd': '2', 't': '2', 'n': '2', 'l': '2',
  67. 'g': '3', 'k': '3', 'h': '3',
  68. 'j': '4', 'q': '4', 'x': '4',
  69. 'zh': '5', 'ch': '5', 'sh': '5', 'r': '5',
  70. 'z': '6', 'c': '6', 's': '6'
  71. }
  72. code = pinyin[0].upper() # 保留第一個字母
  73. for char in pinyin[1:]:
  74. if char in soundex_map:
  75. if len(code) == 1 or code[-1] != soundex_map[char]:
  76. code += soundex_map[char]
  77. if len(code) == 4:
  78. break
  79. return code.ljust(4, '0')
  80. def compare_chinese_words(word1, word2):
  81. pinyin1 = ''.join([p[0] for p in pinyin(word1, style=Style.NORMAL)])
  82. pinyin2 = ''.join([p[0] for p in pinyin(word2, style=Style.NORMAL)])
  83. soundex1 = chinese_soundex(pinyin1)
  84. soundex2 = chinese_soundex(pinyin2)
  85. return soundex1 == soundex2
  86. # 幾個有修正困難的先hard code
  87. error_correction = {
  88. "看拳": "碳權",
  89. "看盤插": "碳盤查",
  90. "盤插": "盤查",
  91. "看":"碳"
  92. }
  93. def fuzzy_correct_chinese(text, correct_terms):
  94. words = jieba.cut(text)
  95. corrected_words = []
  96. for word in words:
  97. # 首先檢查是否在錯誤修正字典中
  98. if word in error_correction:
  99. corrected_words.append(error_correction[word])
  100. else:
  101. # 如果不在錯誤修正字典中,則使用 Soundex 方法。先以自定義字典做諧音比較
  102. for term in correct_terms:
  103. if compare_chinese_words(word, term):
  104. corrected_words.append(term)
  105. break
  106. else:
  107. corrected_words.append(word)
  108. return ' '.join(corrected_words)
  109. ################ 執行 ######################
  110. def post_process_transcript(transcript, temperature=0):
  111. # 定義正確的術語列表
  112. correct_terms = ["碳", "溫室氣體", "碳排放", "排放", "碳管理", "管理", "碳盤查", "盤查", "碳權交易", "碳費"
  113. , "碳權", "碳足跡", "足跡", "淨零排放", "零排放", "排放", "淨零"
  114. , "氣候變遷法", "氣候", "氣候變遷", "法"
  115. , "是什麼", "請解釋", "為什麼", "什麼意思"
  116. , "台灣"
  117. ]
  118. # 使用 Soundex 進行初步校正
  119. corrected_transcript = fuzzy_correct_chinese(transcript, correct_terms)
  120. messages = [
  121. {"role": "system", "content": system_prompt},
  122. {"role": "user", "content": f"請校對並修正以下轉錄文本,但不要改變其原意或回答問題:\n\n{corrected_transcript}"}
  123. ]
  124. response = client.chat.completions.create(
  125. model="gpt-4o", #gpt-4o效果比3.5好很多
  126. temperature=temperature,
  127. messages=messages
  128. )
  129. return response.choices[0].message.content
  130. def main():
  131. parser = argparse.ArgumentParser(description="處理音頻文件使用 Whisper")
  132. parser.add_argument("--file", help="要處理的單個音頻文件的路徑")
  133. parser.add_argument("--folder", default="data", help="包含音頻文件的文件夾路徑(默認:data)")
  134. args = parser.parse_args()
  135. if args.file:
  136. if os.path.isfile(args.file):
  137. process_audio_file(args.file)
  138. else:
  139. print(f"錯誤:文件 '{args.file}' 不存在。")
  140. elif args.folder:
  141. if os.path.isdir(args.folder):
  142. process_folder(args.folder)
  143. else:
  144. print(f"錯誤:文件夾 '{args.folder}' 不存在。")
  145. else:
  146. print("錯誤:請指定一個文件(--file)或文件夾(--folder)來處理。")
  147. if __name__ == "__main__":
  148. main()