汉疆唐土34 2024-12-29 15:33 采纳率: 66.7%
浏览 22
已结题

利用Python开发基于人工智能的音乐创作辅助系统项目时,调用Flask框架,在前端网页按下确认按钮后,出现too many dimensions 'str'提示

问题描述

本人利用Python开发基于人工智能的音乐创作辅助系统项目时,调用Flask框架,在前端网页按下确认按钮后网页前端报错如图所示:

img

编译器终端输出的信息如图所示:

img

编译环境

编译环境版本
Python 版本3.11
PyCharm2023.1.4 (Professional Edition)
Flask2.3.2

主要代码

后端

  • AIMusic/app.py

from flask import Flask, request, jsonify, send_from_directory, url_for, render_template
from flask_socketio import SocketIO
from celery import Celery
from music_logic import MusicLogic
from ai_model import AIModel
from ai_model import CustomModel  # 引入自定义模型类
from storage_manager import StorageManager
import os
import logging

# Flask 初始化
app = Flask(__name__)
# 启用调试模式
app.debug = True
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['STATIC_FOLDER'] = 'static/assets'

# 创建上传和静态目录(如不存在)
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
os.makedirs(app.config['STATIC_FOLDER'], exist_ok=True)

# WebSocket 初始化
socketio = SocketIO(app, cors_allowed_origins="*")

# Celery 初始化
celery_app = Celery('tasks', broker='redis://localhost:6379/0')

# 日志配置
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# StorageManager 实例化
storage_manager = StorageManager(app.config['UPLOAD_FOLDER'])

# 初始化模型架构
ai_model = AIModel(
    model_class=CustomModel,
    input_dim=128,  # 根据你的模型输入维度定义
    hidden_dim=256,  # 隐藏层维度
    output_dim=128   # 输出维度
)
ai_model.load_weights(r"E:\AIMusic\models\best_model.pth")  # 加载初始权重
music_logic = MusicLogic(ai_model)

# 音乐创作页面路由
@app.route('/music_creation')
def music_creation():
    """渲染音乐创作页面"""
    return render_template("music_creation.html")


@app.route('/melody_generation', methods=['GET', 'POST'])
def melody_generation():
    if request.method == 'GET':
        return render_template("melody_generation.html")
    elif request.method == 'POST':
        if not request.is_json:
            return jsonify({'code': 1, 'msg': 'Invalid request format: JSON required.'}), 400

        data = request.get_json()
        melody_length = data.get("length")
        style_name = data.get("style")

        if not isinstance(melody_length, int) or not isinstance(style_name, str):
            return jsonify({'code': 1, 'msg': 'Invalid input types'}), 400

        if melody_length < 1 or melody_length > 128:
            return jsonify({'code': 1, 'msg': 'Length must be between 1 and 128'}), 400

        style = style_mapping.get(style_name)
        if style is None:
            return jsonify({'code': 1, 'msg': 'Invalid style'}), 400

        try:
            melody = music_logic.generate_melody(melody=melody_length, style=style)
            return jsonify({'code': 0, 'msg': 'Melody generated successfully', 'melody': melody})
        except Exception as e:
            logger.error(f"Melody generation failed: {e}")
            return jsonify({'code': 2, 'msg': f'Melody generation failed: {str(e)}'}, 500)

# 和弦编排接口
@app.route('/chord_arrangement', methods=['GET', 'POST'])
def chord_arrangement():
    if request.method == 'GET':
        return render_template("chord_arrangement.html")  # 渲染和弦编排页面
    elif request.method == 'POST':
        data = request.json
        melody = data.get("melody")

        if not melody:
            return jsonify({'code': 1, 'msg': 'Melody is required for chord arrangement'}), 400
        try:
            # 生成和弦
            chords = music_logic.arrange_chords(melody)
            return jsonify({'code': 0, 'msg': 'Chords arranged successfully', 'chords': chords})
        except Exception as e:
            logger.error(f"Chord arrangement failed: {e}")
            return jsonify({'code': 2, 'msg': f'Chord arrangement failed: {str(e)}'})

# 风格定制接口
@app.route('/style_customization', methods=['GET', 'POST'])
def style_customization():
    if request.method == 'GET':
        return render_template("style_customization.html")  # 渲染风格定制页面
    elif request.method == 'POST':
        data = request.json
        style = data.get("style")

        if not style:
            return jsonify({'code': 1, 'msg': 'Style is required for customization'}), 400
        try:
            # 模拟风格定制处理逻辑
            logger.info(f"Customizing style to: {style}")
            return jsonify({'code': 0, 'msg': f'Successfully customized to {style}'})
        except Exception as e:
            logger.error(f"Style customization failed: {e}")
            return jsonify({'code': 2, 'msg': f'Style customization failed: {str(e)}'})

# 系统根目录
@app.route("/")
def home():
    """渲染主页"""
    return render_template("index.html")

# 主程序入口
if __name__ == '__main__':
    print("Starting server on http://0.0.0.0:5000")
    socketio.run(app, host='0.0.0.0', port=5000)
  • AIMusic/aimodel.py
import random
import torch
import logging
import os
import pretty_midi
import fluidsynth

from torch import nn

# 初始化日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class CustomModel(nn.Module):
    def __init__(self, input_dim, hidden_dim, output_dim):
        super(CustomModel, self).__init__()
        self.fc1 = nn.Linear(input_dim, hidden_dim)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_dim, output_dim)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        return x

class AIModel:
    def __init__(self, model_class=None, input_dim=None, hidden_dim=None, output_dim=None):
        """
        初始化 AIModel 实例(仅初始化模型结构)
        :param model_class: PyTorch 模型类
        :param input_dim: 输入维度
        :param hidden_dim: 隐藏层维度
        :param output_dim: 输出维度
        """
        try:
            logger.info("Initializing AIModel...")

            # 如果提供了模型类,则实例化模型
            if model_class:
                # 确保 model_class 是一个类,而不是直接调用自身
                self.model = model_class(input_dim, hidden_dim, output_dim)
                logger.info("Model architecture initialized.")
            else:
                # 如果没有提供模型类,则抛出异常
                raise ValueError("Model class must be provided to initialize AIModel.")

            # 设置设备(支持 GPU)
            self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
            self.model.to(self.device)
        except Exception as e:
            logger.error(f"Failed to initialize AIModel: {str(e)}")
            raise RuntimeError(f"Model initialization failed: {str(e)}")

    def load_weights(self, model_path):
        """
        动态加载模型权重并重建架构
        """
        try:
            if not os.path.exists(model_path):
                raise FileNotFoundError(f"Model file not found: {model_path}")

            # 加载模型文件
            checkpoint = torch.load(model_path, map_location=self.device)

            # 动态重建模型架构
            input_size = checkpoint.get('input_size')
            hidden_size = checkpoint.get('hidden_size')
            output_size = checkpoint.get('output_size')
            if not all([input_size, hidden_size, output_size]):
                raise ValueError("Invalid checkpoint: missing architecture parameters.")

            self.model = CustomModel(input_size, hidden_size, output_size)
            self.model.to(self.device)

            # 加载权重
            state_dict = checkpoint.get('model_state_dict')
            if not state_dict:
                raise ValueError("Invalid checkpoint: missing state_dict.")
            self.model.load_state_dict(state_dict)
            self.model.eval()

            logger.info(f"Model loaded successfully from {model_path}")
        except Exception as e:
            logger.error(f"Failed to load model weights: {str(e)}")
            raise RuntimeError(f"Failed to load model weights: {str(e)}")

  
    def generate(self, melody, style):
        """
        根据输入旋律和风格生成音乐
        :param melody: 输入旋律 (str)
        :param style: 输入风格 (str)
        :return: 生成的音乐数据
        """
        try:
            # 数据预处理
            input_data = self.preprocess(melody, style)
            if not isinstance(input_data, torch.Tensor):
                raise ValueError("Input data must be a torch.Tensor")
            # 确保输入数据是二维张量
            if input_data.dim() != 2:
                raise ValueError("Input data must be a 2D tensor")

            # 模型推理
            with torch.no_grad():
                logger.info(f"Running inference with melody: {melody} and style: {style}")
                output = self.model(input_data)

            # 数据后处理
            result = self.postprocess(output)
            logger.info("Music generation completed successfully.")
            return result
        except Exception as e:
            logger.error(f"Error during music generation: {str(e)}")
            raise RuntimeError(f"Music generation failed: {str(e)}")

    def generate_audio(self, melody, style, file_path):
        # 使用模型生成音乐数据
        music_data = self.generate(melody, style)

        # 创建一个 PrettyMIDI 对象
        midi = pretty_midi.PrettyMIDI()
        # 创建一个乐器实例,这里假设使用钢琴
        program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano')
        instrument = pretty_midi.Instrument(program=program)

        # 填充乐器的音符数据
        for note in music_data:
            note_number = note[0]  # MIDI 音符编号
            start_time = note[1]  # 音符开始时间
            end_time = note[2]  # 音符结束时间
            velocity = note[3]  # 音符力度
            midi_note = pretty_midi.Note(velocity=velocity, pitch=note_number, start=start_time, end=end_time)
            instrument.notes.append(midi_note)

        midi.instruments.append(instrument)
        # 保存 MIDI 文件
        midi.save('temp.mid')

        # 使用 FluidSynth 将 MIDI 转换为 WAV
        fs = fluidsynth.Synth()
        fs.start()
        sfid = fs.sfload('path_to_your_soundfont.sf2')
        fs.program_select(0, sfid, 0, 0)

        fs.midi_to_audio('temp.mid', file_path)
        fs.delete()

    def generate_melody(self, style, length, filename='output.wav'):
        """
        根据音乐风格和长度生成旋律并直接输出音频文件
        :param style: 音乐风格 (str)
        :param length: 旋律长度 (int)
        :param filename: 输出音频文件名 (str)
        :return: None, 直接保存音频文件
        """
        try:
            # 确保长度是整数
            if not isinstance(length, int):
                length = int(length)

            # 调用 generate 方法生成音频文件
            self.generate_audio(self.generate_initial_melody(length), style, filename)

            logger.info("Melody and audio generation successful.")
        except ValueError as ve:
            logger.error(f"Invalid length parameter: {str(ve)}")
            raise RuntimeError(f"Invalid length parameter: {str(ve)}")
        except Exception as e:
            logger.error(f"Error in melody and audio generation: {str(e)}")
            raise RuntimeError(f"Melody and audio generation failed: {str(e)}")

    # def preprocess(self, melody, style):
    #     # 检查输入的旋律是否为 int 类型,如果是,转换为 str 类型
    #     if isinstance(melody, int):
    #         melody = str(melody)
    #     if isinstance(style, int):
    #         style = str(style)
    #     # 以下是可能的预处理器的逻辑,例如将输入拆分为更小的单元
    #     processed_melody = melody.split()
    #     processed_style = style.split()
    #     # 这里可以添加更多的预处理器逻辑
    #     return processed_melody, processed_style

    def generate_initial_melody(self, length):
        """
        生成初始随机旋律,用于作为模型输入
        :param length: 旋律长度 (int)
        :return: 随机生成的初始旋律 (str)
        """
        try:
            if not isinstance(length, int) or length <= 0:
                raise ValueError(f"Length must be a positive integer. Received: {length}")

            notes = ["C", "D", "E", "F", "G", "A", "B"]
            initial_melody = "-".join(random.choices(notes, k=length))
            logger.info(f"Initial melody generated: {initial_melody}")
            return initial_melody
        except ValueError as ve:
            logger.error(f"Invalid length parameter: {str(ve)}")
            raise RuntimeError(f"Failed to generate initial melody: {str(ve)}")
        except Exception as e:
            logger.error(f"Error generating initial melody: {str(e)}")
            raise RuntimeError(f"Failed to generate initial melody: {str(e)}")

    def preprocess(self, melody, style):
        # 检查输入的旋律是否为 int 类型,如果是,转换为 str 类型
        if isinstance(melody, int):
            melody = str(melody)
        if isinstance(style, int):
            style = str(style)

        # 检查旋律和风格是否已经是列表或可迭代对象
        if isinstance(melody, str):
            melody = melody.split()
        if isinstance(style, str):
            style = style.split()

        # 确保返回 Tensor 类型
        return torch.tensor(melody + style)
    def postprocess(self, output):
        # 确保输出是 Tensor 类型
        if not isinstance(output, torch.Tensor):
            output = torch.tensor(output)
        # 这里添加后处理逻辑
        return output

    def encode_melody(self, melody):
        """
        编码旋律
        :param melody: 旋律字符串或列表
        :return: 数值化旋律表示
        """
        note_mapping = {"C": 0, "D": 1, "E": 2, "F": 3, "G": 4, "A": 5, "B": 6}
        return [note_mapping[note] for note in melody.split("-")]

    def encode_style(self, style):
        """
        编码风格
        :param style: 风格字符串
        :return: 数值化风格表示
        """
        style_mapping = {
            "Pop": 0, "Rock": 1, "Jazz": 2, "Classical": 3,
            "Electronic": 4, "Folk": 5, "Hip-Hop": 6,
            "Blues": 7, "Country": 8, "World": 9
        }
        if style not in style_mapping:
            raise ValueError(f"Unsupported style: {style}")
        return style_mapping[style]

    def decode_output(self, output):
        """
        解码模型输出
        :param output: 模型原始输出
        :return: 解码后的旋律或音符序列
        """
        reverse_mapping = {0: "C", 1: "D", 2: "E", 3: "F", 4: "G", 5: "A", 6: "B"}
        return [reverse_mapping[int(value)] for value in output.squeeze().tolist()]


  • AIMusic/musiclogic.py
import os
import time
import logging

# 初始化日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class MusicLogic:
    SUPPORTED_STYLES = [
        "Pop", "Rock", "Jazz", "Classical", "Electronic",
        "Folk", "Hip-Hop", "Blues", "Country", "World"
    ]

    def __init__(self, model, output_dir="output"):
        """
        初始化 MusicLogic 实例
        :param model: AI音乐生成模型实例
        :param output_dir: 生成的音乐文件存储目录
        """
        self.model = model
        self.output_dir = output_dir

        # 确保输出目录存在
        os.makedirs(self.output_dir, exist_ok=True)

    def validate_inputs(self, melody, style):
        """
        验证输入的旋律和风格是否有效。
        :param melody: 原始旋律
        :param style: 音乐风格
        :raises ValueError: 如果输入无效
        """
        if not melody:
            raise ValueError("Melody must be provided.")
        if not style:
            raise ValueError("Style must be provided.")
        if style not in self.SUPPORTED_STYLES:
            raise ValueError(f"Unsupported style '{style}'. Supported styles are: {self.SUPPORTED_STYLES}")

    def generate_melody(self, melody, style):
        try:
            file_name = f"music_{int(time.time())}.wav"
            file_path = os.path.join(self.output_dir, file_name)
            audio_data = self.model.generate_audio(melody, style, file_path)
            with open(file_path, "wb") as f:
                f.write(audio_data)
            return file_path
        except Exception as e:
            logger.error(f"Error during melody generation: {str(e)}")
            raise RuntimeError(f"Error during melody generation: {str(e)}")

    def generate_audio_stream(self, melody, style):
        """
        流式生成音频数据。
        :param melody: 原始旋律(字符串或其他表示形式)
        :param style: 音乐风格(字符串)
        :yield: 音频数据块
        """
        self.validate_inputs(melody, style)

        try:
            logger.info(f"Starting stream generation for melody: {melody} with style: {style}")
            # 调用AI模型生成完整音频
            full_audio = self.model.generate_audio(melody, style)

            # 模拟将音频分成小块流式输出
            chunk_size = len(full_audio) // 10  # 分为10段
            for i in range(10):
                time.sleep(0.1)  # 模拟生成延迟
                start = i * chunk_size
                end = (i + 1) * chunk_size if i < 9 else len(full_audio)
                logger.info(f"Streaming chunk {i + 1}/10")
                yield full_audio[start:end]
        except Exception as e:
            logger.error(f"Error during audio stream generation: {str(e)}")
            raise RuntimeError(f"Error during audio stream generation: {str(e)}")


前端

  • AIMusic/template/melody_generation.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>生成旋律</title>
    <link rel="stylesheet" href="../static/layui/css/layui.css">
    <style>
        /* 自定义样式 */
        body {
            background-image: url("../static/images/background.webp");
            background-size: cover;
            background-position: center;
            background-attachment: fixed;
            opacity: 0;
            animation: fadeIn 0.5s ease-out forwards;
        }

        .layui-card {
            background-color: rgba(255, 255, 255, 0.8);
            border-radius: 10px;
            padding: 20px;
        }

        .layui-card-header {
            font-size: 24px;
            font-weight: bold;
            color: #333;
        }

        .layui-card-body {
            color: #555;
        }

        .footer {
            text-align: center;
            margin-top: 40px;
            color: #666;
            font-size: 14px;
        }

        /* 动画:body 渐显 */
        @keyframes fadeIn {
            from {
                opacity: 0;
            }
            to {
                opacity: 1;
            }
        }
    </style>
</head>
<body>
<div class="layui-container">
    <!-- 页面标题 -->
    <div class="layui-row">
        <div class="layui-col-xs12">
            <div class="layui-card">
                <div class="layui-card-header">生成旋律</div>
                <div class="layui-card-body">
                    <p>请选择旋律风格和长度,然后点击“生成旋律”按钮。</p>

<div class="layui-form">
    <label class="layui-form-label">风格</label>
    <div class="layui-input-block">
        <select id="style" class="layui-select">
            <option value="Pop">流行 (Pop)</option>
            <option value="Rock">摇滚 (Rock)</option>
            <option value="Jazz">爵士 (Jazz)</option>
            <option value="Classical">古典 (Classical)</option>
            <option value="Electronic">电子 (Electronic)</option>
            <option value="Folk">民谣 (Folk)</option>
            <option value="Hip-Hop">嘻哈 (Hip-Hop)</option>
            <option value="Blues">蓝调 (Blues)</option>
            <option value="Country">乡村 (Country)</option>
            <option value="World">世界音乐 (World)</option>
        </select>
    </div>
    <label class="layui-form-label">长度</label>
    <div class="layui-input-block">
        <input id="length" type="number" value="16" class="layui-input" min="1" max="128">
    </div>
    <button id="generate-button" class="layui-btn layui-btn-normal" style="margin-top: 15px;">生成旋律</button>
</div>

                    <!-- 显示生成结果 -->
                    <div id="result" style="margin-top: 20px;"></div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- 页脚 -->
<div class="footer">
    © 2024 贵池威廉 | 基于人工智能的音乐创作辅助系统
</div>

<!-- 引入 Layui JS -->
<script src="../static/layui/layui.js"></script>
<script>

document.getElementById('generate-button').addEventListener('click', function() {
    const style = document.getElementById('style').value.trim();
    const lengthInput = document.getElementById('length').value;
    const length = parseInt(lengthInput, 10);

    if (isNaN(length) || length <= 0) {
        document.getElementById('result').innerHTML = `<p style="color: red;">错误: 长度必须是正整数!</p>`;
        return;
    }

    if (!style) {
        document.getElementById('result').innerHTML = `<p style="color: red;">错误: 风格不能为空!</p>`;
        return;
    }

    fetch('/melody_generation', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            style: style,
            length: length
        })
    })
    .then(response => response.json())
    .then(data => {
        if (data.code === 0) {
            document.getElementById('result').innerHTML = `<p style="color: green;">生成的旋律: ${data.melody.join(', ')}</p>`;
        } else {
            document.getElementById('result').innerHTML = `<p style="color: red;">错误: ${data.msg}</p>`;
        }
    })
    .catch(error => {
        document.getElementById('result').innerHTML = `<p style="color: red;">请求失败: ${error.message}</p>`;
    });
});


// 使用 layui 渲染表单
layui.use(['form'], function() {
    const form = layui.form;
    form.render(); // 渲染表单
});

</script>
</body>
</html>



  • 写回答

19条回答 默认 最新

  • caperxi 2024-12-29 16:31
    关注

    引自文心一言AI大模型:ERNIE-Speed-8K,若有帮助,还望采纳。

    从提供的信息来看,你在使用 Flask 框架开发一个基于人工智能的音乐创作辅助系统时,遇到了一个错误提示:“too many dimensions 'str'”。这个错误提示通常出现在你尝试将一个字符串(str)作为张量(tensor)输入到模型中,而模型期望的是一个多维数组。

    从代码中看,问题可能出在 AIModel 类的 preprocess 方法中。你定义了 preprocess 方法,但似乎没有实际使用,而是使用了一个未定义的 preprocess 方法(在 AIModel 类外部)。这可能导致你在调用 generate 方法时,实际使用的是未定义的 preprocess 方法,从而传入了一个字符串类型的 melodystyle,而模型期望的是张量类型。

    为了解决这个问题,你应该确保在 AIModel 类中定义了 preprocess 方法,并且正确地使用了它。以下是一个可能的解决方案:

    1. AIModel 类中定义 preprocess 方法,确保它返回一个张量:
    def preprocess(self, melody, style):
        # 检查输入的旋律是否为 int 类型,如果是,转换为 str 类型
        if isinstance(melody, int):
            melody = str(melody)
        if isinstance(style, int):
            style = str(style)
    
        # 检查旋律和风格是否已经是列表或可迭代对象
        if isinstance(melody, str):
            melody = melody.split()
        if isinstance(style, str):
            style = style.split()
    
        # 将旋律和风格编码为数值,并合并为一个列表
        melody_encoding = [self.encode_melody(note) for note in melody]
        style_encoding = [self.encode_style(style_type) for style_type in style]
    
        # 确保返回 Tensor 类型
        return torch.tensor(melody_encoding + style_encoding)
    
    1. 确保在 generate 方法中调用正确的 preprocess 方法:
    def generate(self, melody, style):
        # ...
        input_data = self.preprocess(melody, style)
        # ...
    
    1. 如果你的 encode_melodyencode_style 方法需要更多的参数或不同的逻辑,请相应地修改 preprocess 方法。

    希望这个解决方案能帮助你解决问题。如果还有其他问题或需要进一步的帮助,请随时告诉我。

    评论

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 12月29日
  • 创建了问题 12月29日